Merge pull request #3571 from akohlmey/lepton-package
LEPTON package using the Lepton library to compute forces from expression strings
This commit is contained in:
@ -251,6 +251,7 @@ set(STANDARD_PACKAGES
|
||||
KSPACE
|
||||
LATBOLTZ
|
||||
LATTE
|
||||
LEPTON
|
||||
MACHDYN
|
||||
MANIFOLD
|
||||
MANYBODY
|
||||
@ -513,7 +514,7 @@ else()
|
||||
endif()
|
||||
|
||||
foreach(PKG_WITH_INCL KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF
|
||||
PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM LATTE MSCG COMPRESS ML-PACE)
|
||||
PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM LATTE MSCG COMPRESS ML-PACE LEPTON)
|
||||
if(PKG_${PKG_WITH_INCL})
|
||||
include(Packages/${PKG_WITH_INCL})
|
||||
endif()
|
||||
|
||||
@ -2,19 +2,14 @@ set(COLVARS_SOURCE_DIR ${LAMMPS_LIB_SOURCE_DIR}/colvars)
|
||||
|
||||
file(GLOB COLVARS_SOURCES ${COLVARS_SOURCE_DIR}/[^.]*.cpp)
|
||||
|
||||
option(COLVARS_DEBUG "Debugging messages for Colvars (quite verbose)" OFF)
|
||||
option(COLVARS_DEBUG "Enable debugging messages for Colvars (quite verbose)" OFF)
|
||||
|
||||
# Build Lepton by default
|
||||
option(COLVARS_LEPTON "Build and link the Lepton library" ON)
|
||||
option(COLVARS_LEPTON "Use the Lepton library for custom expressions" ON)
|
||||
|
||||
if(COLVARS_LEPTON)
|
||||
set(LEPTON_DIR ${LAMMPS_LIB_SOURCE_DIR}/colvars/lepton)
|
||||
file(GLOB LEPTON_SOURCES ${LEPTON_DIR}/src/[^.]*.cpp)
|
||||
add_library(lepton STATIC ${LEPTON_SOURCES})
|
||||
# Change the define below to LEPTON_BUILDING_SHARED_LIBRARY when linking Lepton as a DLL with MSVC
|
||||
target_compile_definitions(lepton PRIVATE -DLEPTON_BUILDING_STATIC_LIBRARY)
|
||||
set_target_properties(lepton PROPERTIES OUTPUT_NAME lammps_lepton${LAMMPS_MACHINE})
|
||||
target_include_directories(lepton PRIVATE ${LEPTON_DIR}/include)
|
||||
if(NOT LEPTON_SOURCE_DIR)
|
||||
include(Packages/LEPTON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(colvars STATIC ${COLVARS_SOURCES})
|
||||
@ -30,14 +25,11 @@ target_include_directories(colvars PRIVATE ${LAMMPS_SOURCE_DIR})
|
||||
target_link_libraries(lammps PRIVATE colvars)
|
||||
|
||||
if(COLVARS_DEBUG)
|
||||
# Need to export the macro publicly to also affect the proxy
|
||||
# Need to export the macro publicly to be valid in interface code
|
||||
target_compile_definitions(colvars PUBLIC -DCOLVARS_DEBUG)
|
||||
endif()
|
||||
|
||||
if(COLVARS_LEPTON)
|
||||
target_link_libraries(lammps PRIVATE lepton)
|
||||
target_compile_definitions(colvars PRIVATE -DLEPTON)
|
||||
# Disable the line below when linking Lepton as a DLL with MSVC
|
||||
target_compile_definitions(colvars PRIVATE -DLEPTON_USE_STATIC_LIBRARIES)
|
||||
target_include_directories(colvars PUBLIC ${LEPTON_DIR}/include)
|
||||
target_link_libraries(colvars PRIVATE lepton)
|
||||
endif()
|
||||
|
||||
35
cmake/Modules/Packages/LEPTON.cmake
Normal file
35
cmake/Modules/Packages/LEPTON.cmake
Normal file
@ -0,0 +1,35 @@
|
||||
# avoid including this file twice
|
||||
if(LEPTON_SOURCE_DIR)
|
||||
return()
|
||||
endif()
|
||||
set(LEPTON_SOURCE_DIR ${LAMMPS_LIB_SOURCE_DIR}/lepton)
|
||||
|
||||
file(GLOB LEPTON_SOURCES ${LEPTON_SOURCE_DIR}/src/[^.]*.cpp)
|
||||
|
||||
if((CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "amd64") OR
|
||||
(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") OR
|
||||
(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64"))
|
||||
option(LEPTON_ENABLE_JIT "Enable Just-In-Time compiler for Lepton" ON)
|
||||
else()
|
||||
option(LEPTON_ENABLE_JIT "Enable Just-In-Time compiler for Lepton" OFF)
|
||||
endif()
|
||||
|
||||
if(LEPTON_ENABLE_JIT)
|
||||
file(GLOB ASMJIT_SOURCES ${LEPTON_SOURCE_DIR}/asmjit/*/[^.]*.cpp)
|
||||
endif()
|
||||
|
||||
add_library(lepton STATIC ${LEPTON_SOURCES} ${ASMJIT_SOURCES})
|
||||
set_target_properties(lepton PROPERTIES OUTPUT_NAME lammps_lepton${LAMMPS_MACHINE})
|
||||
target_compile_definitions(lepton PUBLIC LEPTON_BUILDING_STATIC_LIBRARY=1)
|
||||
target_include_directories(lepton PUBLIC ${LEPTON_SOURCE_DIR}/include)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
find_library(LIB_RT rt QUIET)
|
||||
target_link_libraries(lepton PUBLIC ${LIB_RT})
|
||||
endif()
|
||||
|
||||
if(LEPTON_ENABLE_JIT)
|
||||
target_compile_definitions(lepton PUBLIC "LEPTON_USE_JIT=1;ASMJIT_BUILD_X86=1;ASMJIT_STATIC=1;ASMJIT_BUILD_RELEASE=1")
|
||||
target_include_directories(lepton PUBLIC ${LEPTON_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
target_link_libraries(lammps PRIVATE lepton)
|
||||
@ -44,6 +44,7 @@ set(ALL_PACKAGES
|
||||
KSPACE
|
||||
LATBOLTZ
|
||||
LATTE
|
||||
LEPTON
|
||||
MACHDYN
|
||||
MANIFOLD
|
||||
MANYBODY
|
||||
|
||||
@ -46,6 +46,7 @@ set(ALL_PACKAGES
|
||||
KSPACE
|
||||
LATBOLTZ
|
||||
LATTE
|
||||
LEPTON
|
||||
MACHDYN
|
||||
MANIFOLD
|
||||
MANYBODY
|
||||
|
||||
@ -36,6 +36,7 @@ set(WIN_PACKAGES
|
||||
INTERLAYER
|
||||
KSPACE
|
||||
LATTE
|
||||
LEPTON
|
||||
MACHDYN
|
||||
MANIFOLD
|
||||
MANYBODY
|
||||
|
||||
@ -35,6 +35,7 @@ set(ALL_PACKAGES
|
||||
GRANULAR
|
||||
INTERLAYER
|
||||
KSPACE
|
||||
LEPTON
|
||||
MACHDYN
|
||||
MANYBODY
|
||||
MC
|
||||
|
||||
@ -13,6 +13,7 @@ set(PACKAGES_WITH_LIB
|
||||
KOKKOS
|
||||
LATBOLTZ
|
||||
LATTE
|
||||
LEPTON
|
||||
MACHDYN
|
||||
MDI
|
||||
MESONT
|
||||
|
||||
@ -31,6 +31,7 @@ set(WIN_PACKAGES
|
||||
GRANULAR
|
||||
INTERLAYER
|
||||
KSPACE
|
||||
LEPTON
|
||||
MANIFOLD
|
||||
MANYBODY
|
||||
MC
|
||||
|
||||
@ -35,7 +35,7 @@ This is the list of packages that may require additional steps.
|
||||
* :ref:`ADIOS <adios>`
|
||||
* :ref:`ATC <atc>`
|
||||
* :ref:`AWPMD <awpmd>`
|
||||
* :ref:`COLVARS <colvars>`
|
||||
* :ref:`COLVARS <colvar>`
|
||||
* :ref:`COMPRESS <compress>`
|
||||
* :ref:`ELECTRODE <electrode>`
|
||||
* :ref:`GPU <gpu>`
|
||||
@ -44,6 +44,7 @@ This is the list of packages that may require additional steps.
|
||||
* :ref:`KIM <kim>`
|
||||
* :ref:`KOKKOS <kokkos>`
|
||||
* :ref:`LATTE <latte>`
|
||||
* :ref:`LEPTON <lepton>`
|
||||
* :ref:`MACHDYN <machdyn>`
|
||||
* :ref:`MDI <mdi>`
|
||||
* :ref:`MESONT <mesont>`
|
||||
@ -876,6 +877,55 @@ library.
|
||||
|
||||
----------
|
||||
|
||||
.. _lepton:
|
||||
|
||||
LEPTON package
|
||||
--------------
|
||||
|
||||
To build with this package, you must build the Lepton library which is
|
||||
included in the LAMMPS source distribution in the ``lib/lepton`` folder.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: CMake build
|
||||
|
||||
This is the recommended build procedure for using Lepton in
|
||||
LAMMPS. No additional settings are normally needed besides
|
||||
``-D PKG_LEPTON=yes``.
|
||||
|
||||
On x86 hardware the Lepton library will also include a just-in-time
|
||||
compiler for faster execution. This is auto detected but can
|
||||
be explicitly disabled by setting ``-D LEPTON_ENABLE_JIT=no``
|
||||
(or enabled by setting it to yes).
|
||||
|
||||
.. tab:: Traditional make
|
||||
|
||||
Before building LAMMPS, one must build the Lepton library in lib/lepton.
|
||||
|
||||
This can be done manually in the same folder by using or adapting
|
||||
one of the provided Makefiles: for example, ``Makefile.serial`` for
|
||||
the GNU C++ compiler, or ``Makefile.mpi`` for the MPI compiler wrapper.
|
||||
The Lepton library is written in C++-11 and thus the C++ compiler
|
||||
may need to be instructed to enable support for that.
|
||||
|
||||
In general, it is safer to use build setting consistent with the
|
||||
rest of LAMMPS. This is best carried out from the LAMMPS src
|
||||
directory using a command like these, which simply invokes the
|
||||
``lib/lepton/Install.py`` script with the specified args:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ make lib-lepton # print help message
|
||||
$ make lib-lepton args="-m serial" # build with GNU g++ compiler (settings as with "make serial")
|
||||
$ make lib-lepton args="-m mpi" # build with default MPI compiler (settings as with "make mpi")
|
||||
|
||||
The "machine" argument of the "-m" flag is used to find a
|
||||
Makefile.machine to use as build recipe.
|
||||
|
||||
The build should produce a ``build`` folder and the library ``lib/lepton/liblmplepton.a``
|
||||
|
||||
----------
|
||||
|
||||
.. _mliap:
|
||||
|
||||
ML-IAP package
|
||||
@ -1221,7 +1271,7 @@ The ATC package requires the MANYBODY package also be installed.
|
||||
.. _awpmd:
|
||||
|
||||
AWPMD package
|
||||
------------------
|
||||
-------------
|
||||
|
||||
.. tabs::
|
||||
|
||||
@ -1269,14 +1319,13 @@ AWPMD package
|
||||
|
||||
----------
|
||||
|
||||
.. _colvars:
|
||||
.. _colvar:
|
||||
|
||||
COLVARS package
|
||||
---------------------------------------
|
||||
---------------
|
||||
|
||||
This package includes the `Colvars library
|
||||
<https://colvars.github.io/>`_ into the LAMMPS distribution, which can
|
||||
be built for the most part with all major versions of the C++ language.
|
||||
This package enables the use of the `Colvars <https://colvars.github.io/>`_
|
||||
module included in the LAMMPS source distribution.
|
||||
|
||||
|
||||
.. tabs::
|
||||
@ -1289,17 +1338,13 @@ be built for the most part with all major versions of the C++ language.
|
||||
|
||||
.. tab:: Traditional make
|
||||
|
||||
Before building LAMMPS, one must build the Colvars library in lib/colvars.
|
||||
As with other libraries distributed with LAMMPS, the Colvars library
|
||||
needs to be built before building the LAMMPS program with the COLVARS
|
||||
package enabled.
|
||||
|
||||
This can be done manually in the same folder by using or adapting
|
||||
one of the provided Makefiles: for example, ``Makefile.g++`` for
|
||||
the GNU C++ compiler. C++11 compatibility may need to be enabled
|
||||
for some older compilers (as is done in the example makefile).
|
||||
|
||||
In general, it is safer to use build setting consistent with the
|
||||
rest of LAMMPS. This is best carried out from the LAMMPS src
|
||||
directory using a command like these, which simply invokes the
|
||||
``lib/colvars/Install.py`` script with the specified args:
|
||||
From the LAMMPS ``src`` directory, this is most easily and safely done
|
||||
via one of the following commands, which implicitly rely on the
|
||||
``lib/colvars/Install.py`` script with optional arguments:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@ -1309,10 +1354,17 @@ be built for the most part with all major versions of the C++ language.
|
||||
make lib-colvars args="-m g++-debug" # build with GNU g++ compiler and colvars debugging enabled
|
||||
|
||||
The "machine" argument of the "-m" flag is used to find a
|
||||
Makefile.machine to use as build recipe. If it does not already
|
||||
exist in ``lib/colvars``, it will be auto-generated by using
|
||||
compiler flags consistent with those parsed from the core LAMMPS
|
||||
makefiles.
|
||||
``Makefile.machine`` file to use as build recipe. If such recipe does
|
||||
not already exist in ``lib/colvars``, suitable settings will be
|
||||
auto-generated consistent with those used in the core LAMMPS makefiles.
|
||||
|
||||
|
||||
.. versionchanged:: TBD
|
||||
|
||||
Please note that Colvars uses the Lepton library, which is now
|
||||
included with the LEPTON package; if you use anything other than
|
||||
the ``make lib-colvars`` command, please make sure to :ref:`build
|
||||
Lepton beforehand <lepton>`.
|
||||
|
||||
Optional flags may be specified as environment variables:
|
||||
|
||||
@ -1321,10 +1373,10 @@ be built for the most part with all major versions of the C++ language.
|
||||
COLVARS_DEBUG=yes make lib-colvars args="-m machine" # Build with debug code (much slower)
|
||||
COLVARS_LEPTON=no make lib-colvars args="-m machine" # Build without Lepton (included otherwise)
|
||||
|
||||
The build should produce two files: the library ``lib/colvars/libcolvars.a``
|
||||
(which also includes Lepton objects if enabled) and the specification file
|
||||
``lib/colvars/Makefile.lammps``. The latter is auto-generated, and normally does
|
||||
not need to be edited.
|
||||
The build should produce two files: the library
|
||||
``lib/colvars/libcolvars.a`` and the specification file
|
||||
``lib/colvars/Makefile.lammps``. The latter is auto-generated,
|
||||
and normally does not need to be edited.
|
||||
|
||||
----------
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ packages:
|
||||
* :ref:`ADIOS <adios>`
|
||||
* :ref:`ATC <atc>`
|
||||
* :ref:`AWPMD <awpmd>`
|
||||
* :ref:`COLVARS <colvars>`
|
||||
* :ref:`COLVARS <colvar>`
|
||||
* :ref:`COMPRESS <compress>`
|
||||
* :ref:`ELECTRODE <electrode>`
|
||||
* :ref:`GPU <gpu>`
|
||||
@ -46,6 +46,7 @@ packages:
|
||||
* :ref:`KIM <kim>`
|
||||
* :ref:`KOKKOS <kokkos>`
|
||||
* :ref:`LATTE <latte>`
|
||||
* :ref:`LEPTON <lepton>`
|
||||
* :ref:`MACHDYN <machdyn>`
|
||||
* :ref:`MDI <mdi>`
|
||||
* :ref:`MESONT <mesont>`
|
||||
|
||||
@ -44,6 +44,7 @@ OPT.
|
||||
* :doc:`harmonic (iko) <bond_harmonic>`
|
||||
* :doc:`harmonic/shift (o) <bond_harmonic_shift>`
|
||||
* :doc:`harmonic/shift/cut (o) <bond_harmonic_shift_cut>`
|
||||
* :doc:`lepton (o) <bond_lepton>`
|
||||
* :doc:`mesocnt <bond_mesocnt>`
|
||||
* :doc:`mm3 <bond_mm3>`
|
||||
* :doc:`morse (o) <bond_morse>`
|
||||
@ -93,6 +94,7 @@ OPT.
|
||||
* :doc:`fourier/simple (o) <angle_fourier_simple>`
|
||||
* :doc:`gaussian <angle_gaussian>`
|
||||
* :doc:`harmonic (iko) <angle_harmonic>`
|
||||
* :doc:`lepton (o) <angle_lepton>`
|
||||
* :doc:`mesocnt <angle_mesocnt>`
|
||||
* :doc:`mm3 <angle_mm3>`
|
||||
* :doc:`quartic (o) <angle_quartic>`
|
||||
@ -127,6 +129,7 @@ OPT.
|
||||
* :doc:`fourier (io) <dihedral_fourier>`
|
||||
* :doc:`harmonic (iko) <dihedral_harmonic>`
|
||||
* :doc:`helix (o) <dihedral_helix>`
|
||||
* :doc:`lepton (o) <dihedral_lepton>`
|
||||
* :doc:`multi/harmonic (o) <dihedral_multi_harmonic>`
|
||||
* :doc:`nharmonic (o) <dihedral_nharmonic>`
|
||||
* :doc:`opls (iko) <dihedral_opls>`
|
||||
|
||||
@ -134,6 +134,8 @@ OPT.
|
||||
* :doc:`lcbop <pair_lcbop>`
|
||||
* :doc:`lebedeva/z <pair_lebedeva_z>`
|
||||
* :doc:`lennard/mdf <pair_mdf>`
|
||||
* :doc:`lepton (o) <pair_lepton>`
|
||||
* :doc:`lepton/coul (o) <pair_lepton>`
|
||||
* :doc:`line/lj <pair_line_lj>`
|
||||
* :doc:`lj/charmm/coul/charmm (giko) <pair_charmm>`
|
||||
* :doc:`lj/charmm/coul/charmm/implicit (ko) <pair_charmm>`
|
||||
|
||||
@ -68,6 +68,7 @@ page gives those details.
|
||||
* :ref:`KSPACE <PKG-KSPACE>`
|
||||
* :ref:`LATBOLTZ <PKG-LATBOLTZ>`
|
||||
* :ref:`LATTE <PKG-LATTE>`
|
||||
* :ref:`LEPTON <PKG-LEPTON>`
|
||||
* :ref:`MACHDYN <PKG-MACHDYN>`
|
||||
* :ref:`MANIFOLD <PKG-MANIFOLD>`
|
||||
* :ref:`MANYBODY <PKG-MANYBODY>`
|
||||
@ -492,22 +493,21 @@ COLVARS package
|
||||
|
||||
**Contents:**
|
||||
|
||||
COLVARS stands for collective variables, which can be used to
|
||||
implement various enhanced sampling methods, including Adaptive
|
||||
Biasing Force, Metadynamics, Steered MD, Umbrella Sampling and
|
||||
Restraints. A :doc:`fix colvars <fix_colvars>` command is implemented
|
||||
which wraps a COLVARS library, which implements these methods.
|
||||
simulations.
|
||||
Colvars stands for collective variables, which can be used to implement
|
||||
various enhanced sampling methods, including Adaptive Biasing Force,
|
||||
Metadynamics, Steered MD, Umbrella Sampling and Restraints. A :doc:`fix
|
||||
colvars <fix_colvars>` command is implemented which wraps a COLVARS
|
||||
library, which implements these methods. simulations.
|
||||
|
||||
**Authors:** The COLVARS library is written and maintained by
|
||||
Giacomo Fiorin (ICMS, Temple University, Philadelphia, PA, USA)
|
||||
and Jerome Henin (LISM, CNRS, Marseille, France), originally for
|
||||
the NAMD MD code, but with portability in mind. Axel Kohlmeyer
|
||||
(Temple U) provided the interface to LAMMPS.
|
||||
**Authors:** The COLVARS library is written and maintained by Giacomo
|
||||
Fiorin (NIH, Bethesda, MD, USA) and Jerome Henin (CNRS, Paris, France),
|
||||
originally for the NAMD MD code, but with portability in mind. Axel
|
||||
Kohlmeyer (Temple U) provided the interface to LAMMPS.
|
||||
|
||||
**Install:**
|
||||
|
||||
This package has :ref:`specific installation instructions <colvars>` on the :doc:`Build extras <Build_extras>` page.
|
||||
This package has :ref:`specific installation instructions <colvar>` on
|
||||
the :doc:`Build extras <Build_extras>` page.
|
||||
|
||||
**Supporting info:**
|
||||
|
||||
@ -516,6 +516,8 @@ This package has :ref:`specific installation instructions <colvars>` on the :doc
|
||||
* src/COLVARS/README
|
||||
* lib/colvars/README
|
||||
* :doc:`fix colvars <fix_colvars>`
|
||||
* :doc:`group2ndx <group2ndx>`
|
||||
* :doc:`ndx2group <group2ndx>`
|
||||
* examples/PACKAGES/colvars
|
||||
|
||||
----------
|
||||
@ -1388,6 +1390,46 @@ the :doc:`Build extras <Build_extras>` page.
|
||||
|
||||
----------
|
||||
|
||||
.. _PKG-LEPTON:
|
||||
|
||||
LEPTON package
|
||||
--------------
|
||||
|
||||
**Contents:**
|
||||
|
||||
Styles for pair, bond, and angle forces that evaluate the potential
|
||||
function from a string using the `Lepton mathematical expression parser
|
||||
<https://simtk.org/projects/lepton>`_. Lepton is a C++ library that is
|
||||
bundled with `OpenMM <https://openmm.org/>`_ and can be used for
|
||||
parsing, evaluating, differentiating, and analyzing mathematical
|
||||
expressions. This is a more lightweight and efficient alternative for
|
||||
evaluating custom potential function to an embedded Python interpreter
|
||||
as used in the :ref:`PYTHON package <PKG-PYTHON>`. On the other hand,
|
||||
since the potentials are evaluated form analytical expressions, they are
|
||||
more precise than what can be done with :ref:`tabulated potentials
|
||||
<tabulate>`.
|
||||
|
||||
**Authors:** Axel Kohlmeyer (Temple U). Lepton itself is developed
|
||||
by Peter Eastman at Stanford University.
|
||||
|
||||
.. versionadded:: TBD
|
||||
|
||||
**Install:**
|
||||
|
||||
This package has :ref:`specific installation instructions <lepton>` on
|
||||
the :doc:`Build extras <Build_extras>` page.
|
||||
|
||||
**Supporting info:**
|
||||
|
||||
* src/LEPTON: filenames -> commands
|
||||
* lib/lepton/README.md
|
||||
* :doc:`pair_style lepton <pair_lepton>`
|
||||
* :doc:`bond_style lepton <bond_lepton>`
|
||||
* :doc:`angle_style lepton <angle_lepton>`
|
||||
* :doc:`dihedral_style lepton <dihedral_lepton>`
|
||||
|
||||
----------
|
||||
|
||||
.. _PKG-MACHDYN:
|
||||
|
||||
MACHDYN package
|
||||
|
||||
@ -238,6 +238,11 @@ whether an extra library is needed to build and use the package:
|
||||
- :doc:`fix latte <fix_latte>`
|
||||
- latte
|
||||
- ext
|
||||
* - :ref:`LEPTON <PKG-LEPTON>`
|
||||
- evaluate strings as potential function
|
||||
- :doc:`pair_style lepton <pair_lepton>`
|
||||
- PACKAGES/lepton
|
||||
- int
|
||||
* - :ref:`MACHDYN <PKG-MACHDYN>`
|
||||
- smoothed Mach dynamics
|
||||
- `SMD User Guide <PDF/MACHDYN_LAMMPS_userguide.pdf>`_
|
||||
|
||||
94
doc/src/angle_lepton.rst
Normal file
94
doc/src/angle_lepton.rst
Normal file
@ -0,0 +1,94 @@
|
||||
.. index:: angle_style lepton
|
||||
.. index:: angle_style lepton/omp
|
||||
|
||||
angle_style lepton command
|
||||
==========================
|
||||
|
||||
Accelerator Variants: *lepton/omp*
|
||||
|
||||
Syntax
|
||||
""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
angle_style lepton
|
||||
|
||||
Examples
|
||||
""""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
angle_style lepton
|
||||
|
||||
angle_coeff 1 120.0 "k*theta^2; k=250.0"
|
||||
angle_coeff 2 90.0 "k2*theta^2 + k3*theta^3 + k4*theta^4; k2=300.0; k3=-100.0; k4=50.0"
|
||||
angle_coeff 3 109.47 "k*theta^2; k=350.0"
|
||||
|
||||
Description
|
||||
"""""""""""
|
||||
|
||||
.. versionadded:: TBD
|
||||
|
||||
Angle style *lepton* computes angular interactions between three atoms
|
||||
with a custom potential function. The potential function must be
|
||||
provided as an expression string using "theta" as the angle variable
|
||||
relative to the reference angle :math:`\theta_0` which is provided as an
|
||||
angle coefficient. For example `"200.0*theta^2"` represents a
|
||||
:doc:`harmonic angle <angle_harmonic>` potential with a force constant
|
||||
*K* of 200.0 energy units:
|
||||
|
||||
.. math::
|
||||
|
||||
U_{angle,i} = K (\theta_i - \theta_0)^2 = K \theta^2 \qquad \theta = \theta_i - \theta_0
|
||||
|
||||
The `Lepton library <https://simtk.org/projects/lepton>`_, that the
|
||||
*lepton* angle style interfaces with, evaluates this expression string
|
||||
at run time to compute the pairwise energy. It also creates an
|
||||
analytical representation of the first derivative of this expression
|
||||
with respect to "theta" and then uses that to compute the force between
|
||||
the angle atoms as defined by the topology data.
|
||||
|
||||
The following coefficients must be defined for each angle type via the
|
||||
:doc:`angle_coeff <angle_coeff>` command as in the example above, or in
|
||||
the data file or restart files read by the :doc:`read_data <read_data>`
|
||||
or :doc:`read_restart <read_restart>` commands:
|
||||
|
||||
* Lepton expression (energy units)
|
||||
* :math:`\theta_0` (degrees)
|
||||
|
||||
The Lepton expression must be either enclosed in quotes or must not
|
||||
contain any whitespace so that LAMMPS recognizes it as a single keyword.
|
||||
More on valid Lepton expressions below. The :math:`\theta_0`
|
||||
coefficient is the "equilibrium angle". It is entered in degrees, but
|
||||
internally converted to radians. Thus the expression must assume
|
||||
"theta" is in radians. The potential energy function in the Lepton
|
||||
expression is shifted in such a way, that the potential energy is 0 for
|
||||
a angle :math:`\theta_i == \theta_0`.
|
||||
|
||||
----------
|
||||
|
||||
.. include:: lepton_expression.rst
|
||||
|
||||
----------
|
||||
|
||||
.. include:: accel_styles.rst
|
||||
|
||||
----------
|
||||
|
||||
Restrictions
|
||||
""""""""""""
|
||||
|
||||
This angle style is part of the LEPTON package and only enabled if LAMMPS
|
||||
was built with this package. See the :doc:`Build package
|
||||
<Build_package>` page for more info.
|
||||
|
||||
Related commands
|
||||
""""""""""""""""
|
||||
|
||||
:doc:`angle_coeff <angle_coeff>`, :doc:`angle_style table <angle_table>`,
|
||||
:doc:`bond_style lepton <bond_lepton>`,:doc:`dihedral_style lepton <dihedral_lepton>`
|
||||
|
||||
Default
|
||||
"""""""
|
||||
|
||||
none
|
||||
@ -10,7 +10,7 @@ Syntax
|
||||
|
||||
angle_style style
|
||||
|
||||
* style = *none* or *zero* or *hybrid* or *amoeba* or *charmm* or *class2* or *class2/p6* or *cosine* or *cosine/buck6d* or *cosine/delta* or *cosine/periodic* or *cosine/shift* or *cosine/shift/exp* or *cosine/squared* or *cross* or *dipole* or *fourier* or *fourier/simple* or *gaussian* or *harmonic* or *mm3* or *quartic* or *spica* or *table*
|
||||
* style = *none* or *zero* or *hybrid* or *amoeba* or *charmm* or *class2* or *class2/p6* or *cosine* or *cosine/buck6d* or *cosine/delta* or *cosine/periodic* or *cosine/shift* or *cosine/shift/exp* or *cosine/squared* or *cross* or *dipole* or *fourier* or *fourier/simple* or *gaussian* or *harmonic* or *lepton* or *mm3* or *quartic* or *spica* or *table*
|
||||
|
||||
Examples
|
||||
""""""""
|
||||
@ -90,6 +90,7 @@ of (g,i,k,o,t) to indicate which accelerated styles exist.
|
||||
* :doc:`fourier/simple <angle_fourier_simple>` - angle with a single cosine term
|
||||
* :doc:`gaussian <angle_gaussian>` - multi-centered Gaussian-based angle potential
|
||||
* :doc:`harmonic <angle_harmonic>` - harmonic angle
|
||||
* :doc:`lepton <angle_lepton>` - angle potential from evaluating a string
|
||||
* :doc:`mesocnt <angle_mesocnt>` - piecewise harmonic and linear angle for bending-buckling of nanotubes
|
||||
* :doc:`mm3 <angle_mm3>` - anharmonic angle
|
||||
* :doc:`quartic <angle_quartic>` - angle with cubic and quartic terms
|
||||
|
||||
92
doc/src/bond_lepton.rst
Normal file
92
doc/src/bond_lepton.rst
Normal file
@ -0,0 +1,92 @@
|
||||
.. index:: bond_style lepton
|
||||
.. index:: bond_style lepton/omp
|
||||
|
||||
bond_style lepton command
|
||||
=========================
|
||||
|
||||
Accelerator Variants: *lepton/omp*
|
||||
|
||||
Syntax
|
||||
""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
bond_style lepton
|
||||
|
||||
Examples
|
||||
""""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
bond_style lepton
|
||||
|
||||
bond_coeff 1 1.5 "k*r^2; k=250.0"
|
||||
bond_coeff 2 1.1 "k2*r^2 + k3*r^3 + k4*r^4; k2=300.0; k3=-100.0; k4=50.0"
|
||||
bond_coeff 3 1.3 "k*r^2; k=350.0"
|
||||
|
||||
Description
|
||||
"""""""""""
|
||||
|
||||
.. versionadded:: TBD
|
||||
|
||||
Bond style *lepton* computes bonded interactions between two atoms with
|
||||
a custom function. The potential function must be provided as an
|
||||
expression string using "r" as the distance variable relative to the
|
||||
reference distance :math:`r_0` which is provided as a bond coefficient.
|
||||
For example `"200.0*r^2"` represents a harmonic potential with a force
|
||||
constant *K* of 200.0 energy units:
|
||||
|
||||
.. math::
|
||||
|
||||
U_{bond,i} = K (r_i - r_0)^2 = K r^2 \qquad r = r_i - r_0
|
||||
|
||||
The `Lepton library <https://simtk.org/projects/lepton>`_, that the
|
||||
*lepton* bond style interfaces with, evaluates this expression string at
|
||||
run time to compute the pairwise energy. It also creates an analytical
|
||||
representation of the first derivative of this expression with respect to
|
||||
"r" and then uses that to compute the force between the atom pairs forming
|
||||
bonds as defined by the topology data.
|
||||
|
||||
The following coefficients must be defined for each bond type via the
|
||||
:doc:`bond_coeff <bond_coeff>` command as in the examples above, or in
|
||||
the data file or restart files read by the :doc:`read_data <read_data>`
|
||||
or :doc:`read_restart <read_restart>` commands:
|
||||
|
||||
* Lepton expression (energy units)
|
||||
* :math:`r_0` (distance)
|
||||
|
||||
The Lepton expression must be either enclosed in quotes or must not
|
||||
contain any whitespace so that LAMMPS recognizes it as a single keyword.
|
||||
More on valid Lepton expressions below. The :math:`r_0` is the
|
||||
"equilibrium distance". The potential energy function in the Lepton
|
||||
expression is shifted in such a way, that the potential energy is 0 for
|
||||
a bond length :math:`r_i == r_0`.
|
||||
|
||||
----------
|
||||
|
||||
.. include:: lepton_expression.rst
|
||||
|
||||
----------
|
||||
|
||||
.. include:: accel_styles.rst
|
||||
|
||||
----------
|
||||
|
||||
Restrictions
|
||||
""""""""""""
|
||||
|
||||
This bond style is part of the LEPTON package and only enabled if LAMMPS
|
||||
was built with this package. See the :doc:`Build package
|
||||
<Build_package>` page for more info.
|
||||
|
||||
Related commands
|
||||
""""""""""""""""
|
||||
|
||||
:doc:`bond_coeff <bond_coeff>`, :doc:`bond_style table <bond_table>`,
|
||||
:doc:`bond_write <bond_write>`, :doc:`angle_style lepton <angle_lepton>`,
|
||||
:doc:`dihedral_style lepton <dihedral_lepton>`
|
||||
|
||||
Default
|
||||
"""""""
|
||||
|
||||
none
|
||||
@ -10,7 +10,7 @@ Syntax
|
||||
|
||||
bond_style style args
|
||||
|
||||
* style = *none* or *zero* or *hybrid* or *bpm/rotational* or *bpm/spring* or *class2* or *fene* or *fene/expand* or *fene/nm* or *gaussian* or *gromos* or *harmonic* or *harmonic/shift* or *harmonic/shift/cut* or *morse* or *nonlinear* or *oxdna/fene* or *oxdena2/fene* or *oxrna2/fene* or *quartic* or *special* or *table*
|
||||
* style = *none* or *zero* or *hybrid* or *bpm/rotational* or *bpm/spring* or *class2* or *fene* or *fene/expand* or *fene/nm* or *gaussian* or *gromos* or *harmonic* or *harmonic/shift* or *harmonic/shift/cut* or *lepton* or *morse* or *nonlinear* or *oxdna/fene* or *oxdena2/fene* or *oxrna2/fene* or *quartic* or *special* or *table*
|
||||
|
||||
* args = none for any style except *hybrid*
|
||||
|
||||
@ -95,6 +95,7 @@ accelerated styles exist.
|
||||
* :doc:`harmonic <bond_harmonic>` - harmonic bond
|
||||
* :doc:`harmonic/shift <bond_harmonic_shift>` - shifted harmonic bond
|
||||
* :doc:`harmonic/shift/cut <bond_harmonic_shift_cut>` - shifted harmonic bond with a cutoff
|
||||
* :doc:`lepton <bond_lepton>` - bond potential from evaluating a string
|
||||
* :doc:`mesocnt <bond_mesocnt>` - Harmonic bond wrapper with parameterization presets for nanotubes
|
||||
* :doc:`mm3 <bond_mm3>` - MM3 anharmonic bond
|
||||
* :doc:`morse <bond_morse>` - Morse bond
|
||||
|
||||
89
doc/src/dihedral_lepton.rst
Normal file
89
doc/src/dihedral_lepton.rst
Normal file
@ -0,0 +1,89 @@
|
||||
.. index:: dihedral_style lepton
|
||||
.. index:: dihedral_style lepton/omp
|
||||
|
||||
dihedral_style lepton command
|
||||
=============================
|
||||
|
||||
Accelerator Variants: *lepton/omp*
|
||||
|
||||
Syntax
|
||||
""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
dihedral_style lepton
|
||||
|
||||
Examples
|
||||
""""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
dihedral_style lepton
|
||||
|
||||
dihedral_coeff 1 "k*(1 + d*cos(n*phi)); k=75.0; d=1; n=2"
|
||||
dihedral_coeff 2 "45*(1-cos(4*phi))"
|
||||
dihedral_coeff 2 "k2*cos(phi) + k3*cos(phi)^2; k2=100.0"
|
||||
dihedral_coeff 3 "k*(phi-phi0)^2; k=85.0; phi0=120.0"
|
||||
|
||||
Description
|
||||
"""""""""""
|
||||
|
||||
.. versionadded:: TBD
|
||||
|
||||
Dihedral style *lepton* computes dihedral interactions between four
|
||||
atoms forming a dihedral angle with a custom potential function. The
|
||||
potential function must be provided as an expression string using "phi"
|
||||
as the dihedral angle variable. For example `"200.0*(phi-120.0)^2"`
|
||||
represents a :doc:`quadratic dihedral <dihedral_quadratic>` potential
|
||||
around a 120 degree dihedral angle with a force constant *K* of 200.0
|
||||
energy units:
|
||||
|
||||
.. math::
|
||||
|
||||
U_{dihedral,i} = K (\phi_i - \phi_0)^2
|
||||
|
||||
The `Lepton library <https://simtk.org/projects/lepton>`_, that the
|
||||
*lepton* dihedral style interfaces with, evaluates this expression
|
||||
string at run time to compute the pairwise energy. It also creates an
|
||||
analytical representation of the first derivative of this expression
|
||||
with respect to "phi" and then uses that to compute the force between
|
||||
the dihedral atoms as defined by the topology data.
|
||||
|
||||
The potential function expression for each dihedral type is provided via the
|
||||
:doc:`dihedral_coeff <dihedral_coeff>` command as in the example above, or in
|
||||
the data file or restart files read by the :doc:`read_data <read_data>`
|
||||
or :doc:`read_restart <read_restart>` commands. The expression is in energy units.
|
||||
|
||||
The Lepton expression must be either enclosed in quotes or must not
|
||||
contain any whitespace so that LAMMPS recognizes it as a single keyword.
|
||||
More on valid Lepton expressions below. Dihedral angles are internally
|
||||
computed in radians and thus the expression must assume "phi" is in
|
||||
radians.
|
||||
|
||||
----------
|
||||
|
||||
.. include:: lepton_expression.rst
|
||||
|
||||
----------
|
||||
|
||||
.. include:: accel_styles.rst
|
||||
|
||||
----------
|
||||
|
||||
Restrictions
|
||||
""""""""""""
|
||||
|
||||
This dihedral style is part of the LEPTON package and only enabled if LAMMPS
|
||||
was built with this package. See the :doc:`Build package
|
||||
<Build_package>` page for more info.
|
||||
|
||||
Related commands
|
||||
""""""""""""""""
|
||||
|
||||
:doc:`dihedral_coeff <dihedral_coeff>`, :doc:`dihedral_style table <dihedral_table>`,
|
||||
:doc:`bond_style lepton <bond_lepton>`, :doc:`angle_style lepton <angle_lepton>`
|
||||
|
||||
Default
|
||||
"""""""
|
||||
|
||||
none
|
||||
@ -10,7 +10,7 @@ Syntax
|
||||
|
||||
dihedral_style style
|
||||
|
||||
* style = *none* or *zero* or *hybrid* or *charmm* or *charmmfsw* or *class2* or *osine/shift/exp* or *fourier* or *harmonic* or *helix* or *multi/harmonic* or *nharmonic* or *opls* or *spherical* or *table* or *table/cut*
|
||||
* style = *none* or *zero* or *hybrid* or *charmm* or *charmmfsw* or *class2* or *cosine/shift/exp* or *fourier* or *harmonic* or *helix* or *lepton* or *multi/harmonic* or *nharmonic* or *opls* or *spherical* or *table* or *table/cut*
|
||||
|
||||
Examples
|
||||
""""""""
|
||||
@ -108,6 +108,7 @@ exist.
|
||||
* :doc:`fourier <dihedral_fourier>` - dihedral with multiple cosine terms
|
||||
* :doc:`harmonic <dihedral_harmonic>` - harmonic dihedral
|
||||
* :doc:`helix <dihedral_helix>` - helix dihedral
|
||||
* :doc:`lepton <dihedral_lepton>` - dihedral potential from evaluating a string
|
||||
* :doc:`multi/harmonic <dihedral_multi_harmonic>` - dihedral with 5 harmonic terms
|
||||
* :doc:`nharmonic <dihedral_nharmonic>` - same as multi-harmonic with N terms
|
||||
* :doc:`opls <dihedral_opls>` - OPLS dihedral
|
||||
|
||||
@ -37,57 +37,64 @@ Description
|
||||
|
||||
This fix interfaces LAMMPS to the collective variables (Colvars)
|
||||
library, which allows to calculate potentials of mean force (PMFs) for
|
||||
any set of colvars, using different sampling methods: currently
|
||||
implemented are the Adaptive Biasing Force (ABF) method, metadynamics,
|
||||
Steered Molecular Dynamics (SMD) and Umbrella Sampling (US) via a
|
||||
flexible harmonic restraint bias.
|
||||
any set of colvars, using sampling methods, including but not limited to
|
||||
Adaptive Biasing Force (ABF), metadynamics (MtD), Steered Molecular
|
||||
Dynamics (SMD) and Umbrella Sampling (US) via a flexible harmonic
|
||||
restraint bias.
|
||||
|
||||
This documentation describes only the fix colvars command itself and
|
||||
LAMMPS specific parts of the code. The full documentation of the
|
||||
colvars library is available as `this supplementary PDF document <PDF/colvars-refman-lammps.pdf>`_
|
||||
This documentation describes only the ``fix colvars`` command itself in
|
||||
a LAMMPS script. The Colvars library is documented via the included
|
||||
`PDF manual <PDF/colvars-refman-lammps.pdf>`_ or at the webpage
|
||||
`https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html
|
||||
<https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html>`_.
|
||||
|
||||
The Colvars library is developed at `https://github.com/colvars/colvars <https://github.com/colvars/colvars>`_
|
||||
A detailed discussion of its implementation is in :ref:`(Fiorin) <Fiorin>`.
|
||||
The Colvars library is developed at `https://github.com/Colvars/colvars
|
||||
<https://github.com/Colvars/colvars>`_ A detailed discussion of its
|
||||
implementation is in :ref:`(Fiorin) <Fiorin>`; additional references are
|
||||
printed at runtime based on specific features being used.
|
||||
|
||||
There are some example scripts for using this package with LAMMPS in the
|
||||
examples/PACKAGES/colvars directory.
|
||||
``examples/PACKAGES/colvars`` directory.
|
||||
|
||||
----------
|
||||
|
||||
The only mandatory argument to the fix is the filename to the colvars
|
||||
input file that contains the input that is independent from the MD
|
||||
program in which the colvars library has been integrated.
|
||||
The only required argument to ``fix colvars`` is the filename to the
|
||||
Colvars configuration file that contains the definition of the variables
|
||||
and any biasing methods applied to them. from the MD program in which
|
||||
the colvars library has been integrated.
|
||||
|
||||
The *group-ID* entry is ignored. The collective variable module will
|
||||
always apply to the entire system and there can only be one instance
|
||||
of the colvars fix at a time. The colvars fix will only communicate
|
||||
the minimum information necessary and the colvars library supports
|
||||
multiple, completely independent collective variables, so there is
|
||||
no restriction to functionality by limiting the number of colvars fixes.
|
||||
The *group-ID* entry is ignored. ``fix colvars`` will always apply to
|
||||
the entire system, but specific atoms will be selected based on
|
||||
selection keywords in the Colvars configuration file or files. There is
|
||||
no need to define multiple ``fix colvars`` instances and it is not
|
||||
allowed.
|
||||
|
||||
The *input* keyword allows to specify a state file that would contain
|
||||
the restart information required in order to continue a calculation from
|
||||
a prerecorded state. Fix colvars records it state in :doc:`binary restart <restart>`
|
||||
files, so when using the :doc:`read_restart <read_restart>` command,
|
||||
this is usually not needed.
|
||||
The *output* keyword allows to specify the prefix of output files
|
||||
generated by Colvars, for example ``output.colvars.traj`` or
|
||||
``output.pmf``.
|
||||
|
||||
The *output* keyword allows to specify the output prefix. All output
|
||||
files generated will use this prefix followed by the ".colvars." and
|
||||
a word like "state" or "traj".
|
||||
The *input* keyword allows to specify an optional state file that
|
||||
contains the restart information needed to continue a previous
|
||||
simulation state. Note, however, that ``fix colvars`` records its state
|
||||
in :doc:`binary restart <restart>` files, so when using the
|
||||
:doc:`read_restart <read_restart>` command, this is usually not needed.
|
||||
|
||||
The *seed* keyword contains the seed for the random number generator
|
||||
that will be used in the colvars module.
|
||||
used by Colvars.
|
||||
|
||||
The *unwrap* keyword controls whether wrapped or unwrapped coordinates
|
||||
are passed to the colvars library for calculation of the collective
|
||||
are passed to the Colvars library for calculation of the collective
|
||||
variables and the resulting forces. The default is *yes*, i.e. to use
|
||||
the image flags to reconstruct the absolute atom positions.
|
||||
Setting this to *no* will use the current local coordinates that are
|
||||
wrapped back into the simulation cell at each re-neighboring instead.
|
||||
the image flags to reconstruct the absolute atom positions. Setting
|
||||
this to *no* will use the current local coordinates that are wrapped
|
||||
back into the simulation cell at each re-neighboring instead. For
|
||||
information about when and how this affects results, please see
|
||||
`https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html#sec:colvar_atom_groups_wrapping
|
||||
<https://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html#sec:colvar_atom_groups_wrapping>`_.
|
||||
|
||||
The *tstat* keyword can be either NULL or the label of a thermostatting
|
||||
fix that thermostats all atoms in the fix colvars group. This will be
|
||||
used to provide the colvars module with the current thermostat target
|
||||
fix that thermostats all atoms in the fix colvars group. This will be
|
||||
used to let Colvars know what is the current thermostat target
|
||||
temperature.
|
||||
|
||||
Restart, fix_modify, output, run start/stop, minimize info
|
||||
@ -95,41 +102,42 @@ Restart, fix_modify, output, run start/stop, minimize info
|
||||
|
||||
This fix writes the current status of the colvars module into
|
||||
:doc:`binary restart files <restart>`. This is in addition to the text
|
||||
mode status file that is written by the colvars module itself and the
|
||||
kind of information in both files is identical.
|
||||
mode ``.colvars.state`` written by Colvars itself and the information in
|
||||
both files is identical.
|
||||
|
||||
The :doc:`fix_modify <fix_modify>` *energy* option is supported by
|
||||
this fix to add the energy change from the biasing force added by
|
||||
Colvars to the global potential energy of the system as part of
|
||||
:doc:`thermodynamic output <thermo_style>`. The default setting for
|
||||
this fix is :doc:`fix_modify energy no <fix_modify>`.
|
||||
The :doc:`fix_modify <fix_modify>` *energy* option is supported by this
|
||||
fix to add the energy change from the biasing force added by Colvars to
|
||||
the global potential energy of the system as part of :doc:`thermodynamic
|
||||
output <thermo_style>`. The default setting for this fix is
|
||||
:doc:`fix_modify energy no <fix_modify>`.
|
||||
|
||||
The *fix_modify configfile <config file>* option allows to add settings
|
||||
from an additional config file to the colvars module. This option can
|
||||
only be used, after the system has been initialized with a :doc:`run <run>`
|
||||
command.
|
||||
The *fix_modify configfile <config file>* option loads Colvars
|
||||
configuration from an additional file. This option can only be used,
|
||||
after the system has been initialized with a :doc:`run <run>` command.
|
||||
|
||||
The *fix_modify config <quoted string>* option allows to add settings
|
||||
from inline strings. Those have to fit on a single line when enclosed
|
||||
in a pair of double quotes ("), or can span multiple lines when bracketed
|
||||
by a pair of triple double quotes (""", like python embedded documentation).
|
||||
from inline strings. Those have to fit on a single line when enclosed in
|
||||
a pair of double quotes ("), or can span multiple lines when bracketed
|
||||
by a pair of triple double quotes (""", like Python embedded
|
||||
documentation).
|
||||
|
||||
This fix computes a global scalar which can be accessed by various
|
||||
:doc:`output commands <Howto_output>`. The scalar is the Colvars
|
||||
energy mentioned above. The scalar value calculated by this fix is
|
||||
:doc:`output commands <Howto_output>`. The scalar is the Colvars energy
|
||||
mentioned above. The scalar value calculated by this fix is
|
||||
"extensive".
|
||||
|
||||
Restrictions
|
||||
""""""""""""
|
||||
|
||||
This fix is part of the COLVARS package. It is only enabled if
|
||||
LAMMPS was built with that package. See the :doc:`Build package
|
||||
<Build_package>` page for more info.
|
||||
``fix colvars`` is provided by the COLVARS package and is only available
|
||||
if LAMMPS was built with that package. Some of the features also
|
||||
require code available from the LEPTON package. See the :doc:`Build
|
||||
package <Build_package>` page for more info.
|
||||
|
||||
There can only be one colvars fix active at a time. Since the interface
|
||||
communicates only the minimum amount of information and colvars module
|
||||
itself can handle an arbitrary number of collective variables, this is
|
||||
not a limitation of functionality.
|
||||
There can only be one Colvars instance defined at a time. Since the
|
||||
interface communicates only the minimum amount of information and the
|
||||
Colvars module itself can handle an arbitrary number of collective
|
||||
variables, this is not a limitation of functionality.
|
||||
|
||||
Related commands
|
||||
""""""""""""""""
|
||||
|
||||
122
doc/src/lepton_expression.rst
Normal file
122
doc/src/lepton_expression.rst
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
Lepton expression syntax and features
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Lepton supports the following operators in expressions:
|
||||
|
||||
.. table_from_list::
|
||||
:columns: 14
|
||||
|
||||
* \+
|
||||
* Add
|
||||
*
|
||||
* \-
|
||||
* Subtract
|
||||
*
|
||||
* \*
|
||||
* Multiply
|
||||
*
|
||||
* \/
|
||||
* Divide
|
||||
*
|
||||
* \^
|
||||
* Power
|
||||
|
||||
The following mathematical functions are available:
|
||||
|
||||
.. table_from_list::
|
||||
:columns: 4
|
||||
|
||||
* sqrt(x)
|
||||
* Square root
|
||||
* exp(x)
|
||||
* Exponential
|
||||
* log(x)
|
||||
* Natural logarithm
|
||||
* sin(x)
|
||||
* Sine (angle in radians)
|
||||
* cos(x)
|
||||
* Cosine (angle in radians)
|
||||
* sec(x)
|
||||
* Secant (angle in radians)
|
||||
* csc(x)
|
||||
* Cosecant (angle in radians)
|
||||
* tan(x)
|
||||
* Tangent (angle in radians)
|
||||
* cot(x)
|
||||
* Cotangent (angle in radians)
|
||||
* asin(x)
|
||||
* Inverse sine (in radians)
|
||||
* acos(x)
|
||||
* Inverse cosine (in radians)
|
||||
* atan(x)
|
||||
* Inverse tangent (in radians)
|
||||
* sinh(x)
|
||||
* Hyperbolic sine
|
||||
* cosh(x)
|
||||
* Hyperbolic cosine
|
||||
* tanh(x)
|
||||
* Hyperbolic tangent
|
||||
* erf(x)
|
||||
* Error function
|
||||
* erfc(x)
|
||||
* Complementary Error function
|
||||
* abs(x)
|
||||
* Absolute value
|
||||
* min(x,y)
|
||||
* Minimum of two values
|
||||
* max(x,y)
|
||||
* Maximum of two values
|
||||
* delta(x)
|
||||
* delta(x) is 1 for `x = 0`, otherwise 0
|
||||
* step(x)
|
||||
* step(x) is 0 for `x < 0`, otherwise 1
|
||||
|
||||
Numbers may be given in either decimal or exponential form. All of the
|
||||
following are valid numbers: `5`, `-3.1`, `1e6`, and `3.12e-2`.
|
||||
|
||||
As an extension to the standard Lepton syntax, it is also possible to
|
||||
use LAMMPS :doc:`variables <variable>` in the format "v_name". Before
|
||||
evaluating the expression, "v_name" will be replaced with the value of
|
||||
the variable "name". This is compatible with all kinds of scalar
|
||||
variables, but not with vectors, arrays, local, or per-atom
|
||||
variables. If necessary, a custom scalar variable needs to be defined
|
||||
that can access the desired (single) item from a non-scalar variable.
|
||||
As an example, the following lines will instruct LAMMPS to ramp
|
||||
the force constant for a harmonic bond from 100.0 to 200.0 during the
|
||||
next run:
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
variable fconst equal ramp(100.0, 200)
|
||||
bond_style lepton
|
||||
bond_coeff 1 1.5 "v_fconst * (r^2)"
|
||||
|
||||
An expression may be followed by definitions for intermediate values that appear in the
|
||||
expression. A semicolon ";" is used as a delimiter between value definitions. For example,
|
||||
the expression:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
a^2+a*b+b^2; a=a1+a2; b=b1+b2
|
||||
|
||||
is exactly equivalent to
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
(a1+a2)^2+(a1+a2)*(b1+b2)+(b1+b2)^2
|
||||
|
||||
The definition of an intermediate value may itself involve other
|
||||
intermediate values. Whitespace and quotation characters ('\'' and '"')
|
||||
are ignored. All uses of a value must appear *before* that value's
|
||||
definition. For efficiency reasons, the expression string is parsed,
|
||||
optimized, and then stored in an internal, pre-parsed representation for
|
||||
evaluation.
|
||||
|
||||
Evaluating a Lepton expression is typically between 2.5 and 5 times
|
||||
slower than the corresponding compiled and optimized C++ code. If
|
||||
additional speed or GPU acceleration (via GPU or KOKKOS) is required,
|
||||
the interaction can be represented as a table. Suitable table files
|
||||
can be created either internally using the :doc:`pair_write <pair_write>`
|
||||
or :doc:`bond_write <bond_write>` command or through the Python scripts
|
||||
in the :ref:`tools/tabulate <tabulate>` folder.
|
||||
159
doc/src/pair_lepton.rst
Normal file
159
doc/src/pair_lepton.rst
Normal file
@ -0,0 +1,159 @@
|
||||
.. index:: pair_style lepton
|
||||
.. index:: pair_style lepton/omp
|
||||
.. index:: pair_style lepton/coul
|
||||
.. index:: pair_style lepton/coul/omp
|
||||
|
||||
pair_style lepton command
|
||||
=========================
|
||||
|
||||
Accelerator Variants: *lepton/omp*, *lepton/coul/comp*
|
||||
|
||||
Syntax
|
||||
""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
pair_style style args
|
||||
|
||||
* style = *lepton* or *lepton/coul*
|
||||
* args = list of arguments for a particular style
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
*lepton* args = cutoff
|
||||
cutoff = global cutoff for the interactions (distance units)
|
||||
*lepton/coul* args = cutoff keyword
|
||||
cutoff = global cutoff for the interactions (distance units)
|
||||
zero or more keywords may be appended
|
||||
keyword = *ewald* or *pppm* or *msm* or *dispersion* or *tip4p*
|
||||
|
||||
Examples
|
||||
""""""""
|
||||
|
||||
.. code-block:: LAMMPS
|
||||
|
||||
pair_style lepton 2.5
|
||||
|
||||
pair_coeff * * "k*((r-r0)^2*step(r0-r)); k=200; r0=1.5" 2.0
|
||||
pair_coeff 1 2 "4.0*eps*((sig/r)^12 - (sig/r)^6);eps=1.0;sig=1.0" 1.12246204830937
|
||||
pair_coeff 2 2 "eps*(2.0*(sig/r)^9 - 3.0*(sig/r)^6);eps=1.0;sig=1.0"
|
||||
|
||||
pair_style lepton/coul 2.5
|
||||
pair_coeff 1 1 "qi*qj/r" 4.0
|
||||
pair_coeff 1 2 "lj+coul; lj=4.0*eps*((sig/r)^12 - (sig/r)^6); eps=1.0; sig=1.0; coul=qi*qj/r"
|
||||
|
||||
pair_style lepton/coul 2.5 pppm
|
||||
kspace_style pppm 1.0e-4
|
||||
pair_coeff 1 1 "qi*qj/r*erfc(alpha*r); alpha=1.067"
|
||||
|
||||
Description
|
||||
"""""""""""
|
||||
|
||||
.. versionadded:: TBD
|
||||
|
||||
Pair styles *lepton* and *lepton/coul* compute pairwise interactions
|
||||
between particles which depend solely on the distance and have a cutoff.
|
||||
The potential function must be provided as an expression string using
|
||||
"r" as the distance variable. With pair style *lepton/coul* one may
|
||||
additionally reference the charges of the two atoms of the pair with
|
||||
"qi" and "qj", respectively. Note that further constants in the
|
||||
expression can be defined in the same string as additional expressions
|
||||
separated by semi-colons as shown in the examples above.
|
||||
|
||||
The expression `"200.0*(r-1.5)^2"` represents a harmonic potential
|
||||
around the pairwise distance :math:`r_0` of 1.5 distance units and a
|
||||
force constant *K* of 200.0 energy units:
|
||||
|
||||
.. math::
|
||||
|
||||
U_{ij} = K (r-r_0)^2
|
||||
|
||||
The expression `"qi*qj/r"` represents a regular Coulombic potential with cutoff:
|
||||
|
||||
.. math::
|
||||
|
||||
U_{ij} = \frac{C q_i q_j}{\epsilon r} \qquad r < r_c
|
||||
|
||||
The `Lepton library <https://simtk.org/projects/lepton>`_, that the
|
||||
*lepton* pair style interfaces with, evaluates this expression string at
|
||||
run time to compute the pairwise energy. It also creates an analytical
|
||||
representation of the first derivative of this expression with respect
|
||||
to "r" and then uses that to compute the force between the pairs of
|
||||
particles within the given cutoff.
|
||||
|
||||
The following coefficients must be defined for each pair of atoms types
|
||||
via the :doc:`pair_coeff <pair_coeff>` command as in the examples above,
|
||||
or in the data file or restart files read by the :doc:`read_data
|
||||
<read_data>` or :doc:`read_restart <read_restart>` commands:
|
||||
|
||||
* Lepton expression (energy units)
|
||||
* cutoff (distance units)
|
||||
|
||||
The Lepton expression must be either enclosed in quotes or must not
|
||||
contain any whitespace so that LAMMPS recognizes it as a single keyword.
|
||||
More on valid Lepton expressions below. The last coefficient is
|
||||
optional; it allows to set the cutoff for a pair of atom types to a
|
||||
different value than the global cutoff.
|
||||
|
||||
For pair style *lepton* only the "lj" value of the :doc:`special_bonds <special_bonds>`
|
||||
settings apply in case the interacting pair is also connected with a bond.
|
||||
The potential energy will *only* be added to the "evdwl" property.
|
||||
|
||||
For pair style *lepton/coul* only the "coul" value of the :doc:`special_bonds <special_bonds>`
|
||||
settings apply in case the interacting pair is also connected with a bond.
|
||||
The potential energy will *only* be added to the "ecoul" property.
|
||||
|
||||
----------
|
||||
|
||||
.. include:: lepton_expression.rst
|
||||
|
||||
----------
|
||||
|
||||
.. include:: accel_styles.rst
|
||||
|
||||
----------
|
||||
|
||||
Mixing, shift, table, tail correction, restart, rRESPA info
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Pair styles *lepton* and *lepton/coul* do not support mixing. Thus,
|
||||
expressions for *all* I,J pairs must be specified explicitly.
|
||||
|
||||
Only pair style *lepton* supports the :doc:`pair_modify shift <pair_modify>`
|
||||
option for shifting the energy of the pair interaction so that it is
|
||||
0 at the cutoff, pair style *lepton/coul* does *not*.
|
||||
|
||||
The :doc:`pair_modify table <pair_modify>` options are not relevant for
|
||||
the these pair styles.
|
||||
|
||||
These pair styles do not support the :doc:`pair_modify tail
|
||||
<pair_modify>` option for adding long-range tail corrections to energy
|
||||
and pressure.
|
||||
|
||||
These pair styles write its information to :doc:`binary restart files
|
||||
<restart>`, so pair_style and pair_coeff commands do not need to be
|
||||
specified in an input script that reads a restart file.
|
||||
|
||||
These pair styles can only be used via the *pair* keyword of the
|
||||
:doc:`run_style respa <run_style>` command. They do not support the
|
||||
*inner*, *middle*, *outer* keywords.
|
||||
|
||||
----------
|
||||
|
||||
Restrictions
|
||||
""""""""""""
|
||||
|
||||
These pair styles are part of the LEPTON package and only enabled if
|
||||
LAMMPS was built with this package. See the :doc:`Build package
|
||||
<Build_package>` page for more info.
|
||||
|
||||
Related commands
|
||||
""""""""""""""""
|
||||
|
||||
:doc:`pair_coeff <pair_coeff>`, :doc:`pair_style python <pair_python>`,
|
||||
:doc:`pair_style table <pair_table>`, :doc:`pair_write <pair_write>`
|
||||
|
||||
Default
|
||||
"""""""
|
||||
|
||||
none
|
||||
@ -212,6 +212,8 @@ accelerated styles exist.
|
||||
* :doc:`lcbop <pair_lcbop>` - long-range bond-order potential (LCBOP)
|
||||
* :doc:`lebedeva/z <pair_lebedeva_z>` - Lebedeva interlayer potential for graphene with normals along z-axis
|
||||
* :doc:`lennard/mdf <pair_mdf>` - LJ potential in A/B form with a taper function
|
||||
* :doc:`lepton <pair_lepton>` - pair potential from evaluating a string
|
||||
* :doc:`lepton/coul <pair_lepton>` - pair potential from evaluating a string with support for charges
|
||||
* :doc:`line/lj <pair_line_lj>` - LJ potential between line segments
|
||||
* :doc:`list <pair_list>` - potential between pairs of atoms explicitly listed in an input file
|
||||
* :doc:`lj/charmm/coul/charmm <pair_charmm>` - CHARMM potential with cutoff Coulomb
|
||||
|
||||
@ -554,6 +554,7 @@ corotate
|
||||
corotation
|
||||
corotational
|
||||
correlator
|
||||
Cosecant
|
||||
cosineshifted
|
||||
cossq
|
||||
costheta
|
||||
@ -586,6 +587,7 @@ Crozier
|
||||
Cryst
|
||||
Crystallogr
|
||||
Csanyi
|
||||
csc
|
||||
csg
|
||||
csh
|
||||
cshrc
|
||||
@ -2236,7 +2238,7 @@ msm
|
||||
msmflag
|
||||
msse
|
||||
msst
|
||||
Mtchell
|
||||
MtD
|
||||
Mth
|
||||
mtk
|
||||
Mtotal
|
||||
@ -3277,6 +3279,7 @@ Simul
|
||||
simulations
|
||||
Sinkovits
|
||||
Sinnott
|
||||
sinh
|
||||
sinusoid
|
||||
sinusoidally
|
||||
SiO
|
||||
|
||||
@ -33,6 +33,8 @@ kim hooks to the KIM library, used by KIM package
|
||||
from Ryan Elliott and Ellad Tadmor (U Minn)
|
||||
kokkos Kokkos package for GPU and many-core acceleration
|
||||
from Kokkos development team (Sandia)
|
||||
lepton Lepton library for fast evaluation of mathematical
|
||||
expressions from a string. Imported from OpenMM.
|
||||
linalg set of BLAS and LAPACK routines needed by ATC package
|
||||
from Axel Kohlmeyer (Temple U)
|
||||
mdi hooks to the MDI library, used by MDI package
|
||||
|
||||
@ -61,29 +61,25 @@ COLVARS_SRCS = \
|
||||
colvarvalue.cpp \
|
||||
colvar_neuralnetworkcompute.cpp
|
||||
|
||||
LEPTON_SRCS = \
|
||||
lepton/src/CompiledExpression.cpp \
|
||||
lepton/src/CompiledVectorExpression.cpp \
|
||||
lepton/src/ExpressionProgram.cpp \
|
||||
lepton/src/ExpressionTreeNode.cpp \
|
||||
lepton/src/Operation.cpp \
|
||||
lepton/src/ParsedExpression.cpp \
|
||||
lepton/src/Parser.cpp
|
||||
|
||||
# Allow to selectively turn off Lepton
|
||||
ifeq ($(COLVARS_LEPTON),no)
|
||||
|
||||
LEPTON_INCFLAGS =
|
||||
COLVARS_OBJS = $(COLVARS_SRCS:.cpp=.o)
|
||||
|
||||
else
|
||||
LEPTON_INCFLAGS = -Ilepton/include -DLEPTON
|
||||
COLVARS_OBJS = $(COLVARS_SRCS:.cpp=.o) $(LEPTON_SRCS:.cpp=.o)
|
||||
|
||||
LEPTON_DIR = ../lepton
|
||||
include $(LEPTON_DIR)/Settings.mk
|
||||
LEPTON_INCFLAGS = $(LEPTON_INC) $(LEPTON_DEF)
|
||||
|
||||
endif
|
||||
|
||||
COLVARS_OBJS = $(COLVARS_SRCS:.cpp=.o)
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(COLVARS_INCFLAGS) $(LEPTON_INCFLAGS) -c -o $@ $<
|
||||
|
||||
$(COLVARS_LIB): Makefile.deps $(COLVARS_OBJS)
|
||||
$(COLVARS_LIB): Makefile.deps $(COLVARS_OBJS)
|
||||
$(AR) $(ARFLAGS) $(COLVARS_LIB) $(COLVARS_OBJS)
|
||||
|
||||
|
||||
@ -97,12 +93,3 @@ Makefile.deps: $(COLVARS_SRCS)
|
||||
|
||||
include Makefile.deps
|
||||
|
||||
Makefile.lepton.deps: $(LEPTON_SRCS)
|
||||
@echo > $@
|
||||
@for src in $^ ; do \
|
||||
obj=`basename $$src .cpp`.o ; \
|
||||
$(CXX) $(CXXFLAGS) -MM $(LEPTON_INCFLAGS) \
|
||||
-MT '$$(COLVARS_OBJ_DIR)'$$obj $$src >> $@ ; \
|
||||
done
|
||||
|
||||
include Makefile.lepton.deps
|
||||
|
||||
@ -5,327 +5,367 @@ $(COLVARS_OBJ_DIR)colvaratoms.o: colvaratoms.cpp colvarmodule.h \
|
||||
colvaratoms.h colvardeps.h
|
||||
$(COLVARS_OBJ_DIR)colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h \
|
||||
colvars_version.h colvar.h colvarvalue.h colvartypes.h colvarparse.h \
|
||||
colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarbias_abf.h colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvarbias.h colvargrid.h colvar_UIestimator.h
|
||||
colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarbias_abf.h colvarproxy.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvarbias.h colvargrid.h \
|
||||
colvar_UIestimator.h
|
||||
$(COLVARS_OBJ_DIR)colvarbias_alb.o: colvarbias_alb.cpp colvarmodule.h \
|
||||
colvars_version.h colvarbias.h colvar.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarbias_alb.h
|
||||
colvarparse.h colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarbias_alb.h
|
||||
$(COLVARS_OBJ_DIR)colvarbias.o: colvarbias.cpp colvarmodule.h \
|
||||
colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvarbias.h colvar.h \
|
||||
colvarparse.h colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvargrid.h
|
||||
colvarparse.h colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvargrid.h
|
||||
$(COLVARS_OBJ_DIR)colvarbias_histogram.o: colvarbias_histogram.cpp \
|
||||
colvarmodule.h colvars_version.h colvarproxy.h colvartypes.h \
|
||||
colvarvalue.h colvarproxy_tcl.h colvarproxy_volmaps.h colvar.h \
|
||||
colvarparse.h colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarbias_histogram.h colvarbias.h colvargrid.h
|
||||
colvarparse.h colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarbias_histogram.h colvarbias.h \
|
||||
colvargrid.h
|
||||
$(COLVARS_OBJ_DIR)colvarbias_histogram_reweight_amd.o: \
|
||||
colvarbias_histogram_reweight_amd.cpp \
|
||||
colvarbias_histogram_reweight_amd.h colvarbias_histogram.h colvarbias.h \
|
||||
colvar.h colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvargrid.h colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h
|
||||
colvarparse.h colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvargrid.h colvarproxy.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h
|
||||
$(COLVARS_OBJ_DIR)colvarbias_meta.o: colvarbias_meta.cpp colvarmodule.h \
|
||||
colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvar.h colvarparse.h \
|
||||
colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarbias_meta.h colvarbias.h colvargrid.h
|
||||
colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarbias_meta.h colvarbias.h \
|
||||
colvargrid.h
|
||||
$(COLVARS_OBJ_DIR)colvarbias_restraint.o: colvarbias_restraint.cpp \
|
||||
colvarmodule.h colvars_version.h colvarproxy.h colvartypes.h \
|
||||
colvarvalue.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvarbias_restraint.h colvarbias.h colvar.h colvarparse.h \
|
||||
colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h
|
||||
colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_alchlambda.o: colvarcomp_alchlambda.cpp \
|
||||
colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvar.h colvardeps.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_angles.o: colvarcomp_angles.cpp \
|
||||
colvarmodule.h colvars_version.h colvar.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
colvarparse.h colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_apath.o: colvarcomp_apath.cpp colvarmodule.h \
|
||||
colvars_version.h colvarvalue.h colvartypes.h colvarparse.h \
|
||||
colvarparams.h colvar.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
colvarparams.h colvar.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_coordnums.o: colvarcomp_coordnums.cpp \
|
||||
colvarmodule.h colvars_version.h colvarparse.h colvarvalue.h \
|
||||
colvartypes.h colvarparams.h colvaratoms.h colvarproxy.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvardeps.h colvar.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvar_arithmeticpath.h \
|
||||
colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp.o: colvarcomp.cpp colvarmodule.h \
|
||||
colvars_version.h colvarvalue.h colvartypes.h colvar.h colvarparse.h \
|
||||
colvarparams.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
colvarparams.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_distances.o: colvarcomp_distances.cpp \
|
||||
colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvar.h colvardeps.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_gpath.o: colvarcomp_gpath.cpp colvarmodule.h \
|
||||
colvars_version.h colvarvalue.h colvartypes.h colvarparse.h \
|
||||
colvarparams.h colvar.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
colvarparams.h colvar.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_neuralnetwork.o: \
|
||||
colvarcomp_neuralnetwork.cpp colvarmodule.h colvars_version.h \
|
||||
colvarvalue.h colvartypes.h colvarparse.h colvarparams.h colvar.h \
|
||||
colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h \
|
||||
colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h \
|
||||
colvar_neuralnetworkcompute.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_combination.o: colvarcomp_combination.cpp \
|
||||
colvarcomp.h colvarmodule.h colvars_version.h colvar.h colvarvalue.h \
|
||||
colvartypes.h colvarparse.h colvarparams.h colvardeps.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvaratoms.h colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvaratoms.h colvarproxy.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvar_arithmeticpath.h \
|
||||
colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_protein.o: colvarcomp_protein.cpp \
|
||||
colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvar.h colvardeps.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_rotations.o: colvarcomp_rotations.cpp \
|
||||
colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvar.h colvardeps.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvarcomp_volmaps.o: colvarcomp_volmaps.cpp \
|
||||
colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \
|
||||
colvarparse.h colvarparams.h colvar.h colvardeps.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h
|
||||
$(COLVARS_OBJ_DIR)colvar.o: colvar.cpp colvarmodule.h colvars_version.h \
|
||||
colvarvalue.h colvartypes.h colvarparse.h colvarparams.h colvar.h \
|
||||
colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h \
|
||||
colvarscript.h colvarbias.h colvarscript_commands.h \
|
||||
colvarscript_commands_colvar.h colvarscript_commands_bias.h
|
||||
colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h colvarscript.h \
|
||||
colvarbias.h colvarscript_commands.h colvarscript_commands_colvar.h \
|
||||
colvarscript_commands_bias.h
|
||||
$(COLVARS_OBJ_DIR)colvardeps.o: colvardeps.cpp colvarmodule.h \
|
||||
colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvardeps.h colvarparse.h \
|
||||
colvarparams.h
|
||||
$(COLVARS_OBJ_DIR)colvargrid.o: colvargrid.cpp colvarmodule.h \
|
||||
colvars_version.h colvarvalue.h colvartypes.h colvarparse.h \
|
||||
colvarparams.h colvar.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h \
|
||||
colvargrid.h
|
||||
colvarparams.h colvar.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarcomp.h colvaratoms.h \
|
||||
colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h colvargrid.h
|
||||
$(COLVARS_OBJ_DIR)colvarmodule.o: colvarmodule.cpp colvarmodule.h \
|
||||
colvars_version.h colvarparse.h colvarvalue.h colvartypes.h \
|
||||
colvarparams.h colvarproxy.h colvarproxy_tcl.h colvarproxy_volmaps.h \
|
||||
colvar.h colvardeps.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarbias.h colvarbias_abf.h colvargrid.h colvar_UIestimator.h \
|
||||
colvarbias_alb.h colvarbias_histogram.h \
|
||||
colvarbias_histogram_reweight_amd.h colvarbias_meta.h \
|
||||
colvarbias_restraint.h colvarscript.h colvarscript_commands.h \
|
||||
colvarscript_commands_colvar.h colvarscript_commands_bias.h \
|
||||
colvaratoms.h colvarcomp.h colvar_arithmeticpath.h \
|
||||
colvar_geometricpath.h colvarmodule_refs.h
|
||||
colvar.h colvardeps.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarbias.h colvarbias_abf.h \
|
||||
colvargrid.h colvar_UIestimator.h colvarbias_alb.h \
|
||||
colvarbias_histogram.h colvarbias_histogram_reweight_amd.h \
|
||||
colvarbias_meta.h colvarbias_restraint.h colvarscript.h \
|
||||
colvarscript_commands.h colvarscript_commands_colvar.h \
|
||||
colvarscript_commands_bias.h colvaratoms.h colvarcomp.h \
|
||||
colvar_arithmeticpath.h colvar_geometricpath.h colvarmodule_refs.h
|
||||
$(COLVARS_OBJ_DIR)colvarparams.o: colvarparams.cpp colvarmodule.h \
|
||||
colvars_version.h colvarvalue.h colvartypes.h colvarparams.h
|
||||
$(COLVARS_OBJ_DIR)colvarparse.o: colvarparse.cpp colvarmodule.h \
|
||||
@ -335,17 +375,19 @@ $(COLVARS_OBJ_DIR)colvarproxy.o: colvarproxy.cpp colvarmodule.h \
|
||||
colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvarscript.h colvarbias.h \
|
||||
colvar.h colvarparse.h colvarparams.h colvardeps.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarscript_commands.h colvarscript_commands_colvar.h \
|
||||
colvarscript_commands_bias.h colvaratoms.h colvarmodule_utils.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarscript_commands.h \
|
||||
colvarscript_commands_colvar.h colvarscript_commands_bias.h \
|
||||
colvaratoms.h colvarmodule_utils.h
|
||||
$(COLVARS_OBJ_DIR)colvarproxy_replicas.o: colvarproxy_replicas.cpp \
|
||||
colvarmodule.h colvars_version.h colvarproxy.h colvartypes.h \
|
||||
colvarvalue.h colvarproxy_tcl.h colvarproxy_volmaps.h
|
||||
@ -360,64 +402,68 @@ $(COLVARS_OBJ_DIR)colvarscript.o: colvarscript.cpp colvarproxy.h \
|
||||
colvarmodule.h colvars_version.h colvartypes.h colvarvalue.h \
|
||||
colvarproxy_tcl.h colvarproxy_volmaps.h colvardeps.h colvarparse.h \
|
||||
colvarparams.h colvarscript.h colvarbias.h colvar.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarscript_commands.h colvarscript_commands_colvar.h \
|
||||
colvarscript_commands_bias.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarscript_commands.h \
|
||||
colvarscript_commands_colvar.h colvarscript_commands_bias.h
|
||||
$(COLVARS_OBJ_DIR)colvarscript_commands.o: colvarscript_commands.cpp \
|
||||
colvarproxy.h colvarmodule.h colvars_version.h colvartypes.h \
|
||||
colvarvalue.h colvarproxy_tcl.h colvarproxy_volmaps.h colvardeps.h \
|
||||
colvarparse.h colvarparams.h colvarscript.h colvarbias.h colvar.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarscript_commands.h colvarscript_commands_colvar.h \
|
||||
colvarscript_commands_bias.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarscript_commands.h \
|
||||
colvarscript_commands_colvar.h colvarscript_commands_bias.h
|
||||
$(COLVARS_OBJ_DIR)colvarscript_commands_bias.o: \
|
||||
colvarscript_commands_bias.cpp colvarproxy.h colvarmodule.h \
|
||||
colvars_version.h colvartypes.h colvarvalue.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvardeps.h colvarparse.h colvarparams.h \
|
||||
colvarscript.h colvarbias.h colvar.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarscript_commands.h colvarscript_commands_colvar.h \
|
||||
colvarscript_commands_bias.h
|
||||
colvarscript.h colvarbias.h colvar.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarscript_commands.h \
|
||||
colvarscript_commands_colvar.h colvarscript_commands_bias.h
|
||||
$(COLVARS_OBJ_DIR)colvarscript_commands_colvar.o: \
|
||||
colvarscript_commands_colvar.cpp colvarproxy.h colvarmodule.h \
|
||||
colvars_version.h colvartypes.h colvarvalue.h colvarproxy_tcl.h \
|
||||
colvarproxy_volmaps.h colvardeps.h colvarparse.h colvarparams.h \
|
||||
colvarscript.h colvarbias.h colvar.h lepton/include/Lepton.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarscript_commands.h colvarscript_commands_colvar.h \
|
||||
colvarscript_commands_bias.h
|
||||
colvarscript.h colvarbias.h colvar.h ../lepton/include/Lepton.h \
|
||||
../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarscript_commands.h \
|
||||
colvarscript_commands_colvar.h colvarscript_commands_bias.h
|
||||
$(COLVARS_OBJ_DIR)colvartypes.o: colvartypes.cpp colvarmodule.h \
|
||||
colvars_version.h colvartypes.h colvarparse.h colvarvalue.h \
|
||||
colvarparams.h ../../src/math_eigen_impl.h
|
||||
@ -425,14 +471,15 @@ $(COLVARS_OBJ_DIR)colvarvalue.o: colvarvalue.cpp colvarmodule.h \
|
||||
colvars_version.h colvarvalue.h colvartypes.h
|
||||
$(COLVARS_OBJ_DIR)colvar_neuralnetworkcompute.o: \
|
||||
colvar_neuralnetworkcompute.cpp colvar_neuralnetworkcompute.h \
|
||||
lepton/include/Lepton.h lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h lepton/include/lepton/Parser.h \
|
||||
colvarparse.h colvarmodule.h colvars_version.h colvarvalue.h \
|
||||
colvartypes.h colvarparams.h
|
||||
../lepton/include/Lepton.h ../lepton/include/lepton/CompiledExpression.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/windowsIncludes.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/ExpressionProgram.h \
|
||||
../lepton/include/lepton/ExpressionTreeNode.h \
|
||||
../lepton/include/lepton/Operation.h \
|
||||
../lepton/include/lepton/CustomFunction.h \
|
||||
../lepton/include/lepton/Exception.h \
|
||||
../lepton/include/lepton/ParsedExpression.h \
|
||||
../lepton/include/lepton/Parser.h colvarparse.h colvarmodule.h \
|
||||
colvars_version.h colvarvalue.h colvartypes.h colvarparams.h
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
|
||||
$(COLVARS_OBJ_DIR)CompiledExpression.o: lepton/src/CompiledExpression.cpp \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h
|
||||
$(COLVARS_OBJ_DIR)CompiledVectorExpression.o: \
|
||||
lepton/src/CompiledVectorExpression.cpp \
|
||||
lepton/include/lepton/CompiledVectorExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h
|
||||
$(COLVARS_OBJ_DIR)ExpressionProgram.o: lepton/src/ExpressionProgram.cpp \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h
|
||||
$(COLVARS_OBJ_DIR)ExpressionTreeNode.o: lepton/src/ExpressionTreeNode.cpp \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/Exception.h lepton/include/lepton/Operation.h \
|
||||
lepton/include/lepton/CustomFunction.h lepton/include/lepton/Exception.h
|
||||
$(COLVARS_OBJ_DIR)Operation.o: lepton/src/Operation.cpp \
|
||||
lepton/include/lepton/Operation.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h lepton/src/MSVC_erfc.h
|
||||
$(COLVARS_OBJ_DIR)ParsedExpression.o: lepton/src/ParsedExpression.cpp \
|
||||
lepton/include/lepton/ParsedExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CompiledExpression.h \
|
||||
lepton/include/lepton/CompiledVectorExpression.h \
|
||||
lepton/include/lepton/ExpressionProgram.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h
|
||||
$(COLVARS_OBJ_DIR)Parser.o: lepton/src/Parser.cpp \
|
||||
lepton/include/lepton/Parser.h lepton/include/lepton/windowsIncludes.h \
|
||||
lepton/include/lepton/CustomFunction.h lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h \
|
||||
lepton/include/lepton/Operation.h lepton/include/lepton/CustomFunction.h \
|
||||
lepton/include/lepton/Exception.h \
|
||||
lepton/include/lepton/ParsedExpression.h \
|
||||
lepton/include/lepton/ExpressionTreeNode.h
|
||||
@ -35,39 +35,32 @@ The reference article is:
|
||||
The Colvars library can be built for the most part with all major versions of
|
||||
the C++ language.
|
||||
|
||||
A few of the most recent features require C++11 support. In particular, the
|
||||
library is optionally built together with the
|
||||
"Lepton"_https://simtk.org/projects/lepton library, a copy of which is also
|
||||
included in the LAMMPS distribution. Lepton implements the
|
||||
"customFunction"_http://colvars.github.io/colvars-refman-lammps/colvars-refman-lammps.html#colvar|customFunction
|
||||
feature, and requires C++11 support.
|
||||
|
||||
See "here"_https://colvars.github.io/README-c++11.html for a detailed list of
|
||||
C++11-only features.
|
||||
|
||||
A few of the most recent features require C++11 support, which is also required
|
||||
by LAMMPS, so no additional notes are needed.
|
||||
|
||||
## How to build (CMake)
|
||||
|
||||
This is the recommended build recipe: no additional settings are normally
|
||||
needed besides "-D PKG_COLVARS=yes".
|
||||
|
||||
Building and linking of Lepton (or other C++11-only features) is enabled
|
||||
automatically when compilation is carried out with C++11 support, and disabled
|
||||
otherwise. Optionally, Lepton build may be manually controlled with the flag
|
||||
"-D COLVARS_LEPTON=yes|no".
|
||||
|
||||
Linking to the Lepton library, which is also used by the LEPTON LAMMPS package,
|
||||
is enabled automatically. Optionally, support for Lepton within Colvars may
|
||||
be manually controlled with theCMake setting "-D COLVARS_LEPTON=yes|no".
|
||||
|
||||
## How to build (traditional make)
|
||||
|
||||
Before building LAMMPS, one must build the Colvars library in lib/colvars.
|
||||
Before building LAMMPS, one must build the Colvars library in lib/colvars
|
||||
and the Lepton library in lib/lepton. For building Lepton please see the
|
||||
README.md file in the lib/lepton folder.
|
||||
|
||||
This can be done manually in the same folder by using or adapting one of the
|
||||
provided Makefiles: for example, Makefile.g++ for the GNU compiler.
|
||||
Building the Colvars library can be done manually in the respective
|
||||
folders by using or adapting one of the provided Makefiles: for example,
|
||||
Makefile.g++ for the GNU compiler.
|
||||
|
||||
In general, it is safer to use build setting consistent with the rest of
|
||||
LAMMPS. This is best carried out from the LAMMPS src directory using a
|
||||
command like these, which simply invoke the lib/colvars/Install.py script with
|
||||
the specified args:
|
||||
command like these, which simply invoke the lib/colvars/Install.py script
|
||||
with the specified args:
|
||||
|
||||
make lib-colvars # print help message
|
||||
make lib-colvars args="-m serial" # build with GNU g++ compiler (settings as with "make serial")
|
||||
|
||||
123
lib/lepton/Common.mk
Normal file
123
lib/lepton/Common.mk
Normal file
@ -0,0 +1,123 @@
|
||||
# -*- makefile -*-
|
||||
# common settings for Lepton library makefiles
|
||||
|
||||
SRC= \
|
||||
src/CompiledExpression.cpp \
|
||||
src/CompiledVectorExpression.cpp \
|
||||
src/ExpressionProgram.cpp \
|
||||
src/ExpressionTreeNode.cpp \
|
||||
src/Operation.cpp \
|
||||
src/ParsedExpression.cpp \
|
||||
src/Parser.cpp
|
||||
OBJ=$(SRC:src/%.cpp=build/lepton.%.o)
|
||||
|
||||
JITARM= \
|
||||
asmjit/arm/a64assembler.cpp \
|
||||
asmjit/arm/a64builder.cpp \
|
||||
asmjit/arm/a64compiler.cpp \
|
||||
asmjit/arm/a64emithelper.cpp \
|
||||
asmjit/arm/a64formatter.cpp \
|
||||
asmjit/arm/a64func.cpp \
|
||||
asmjit/arm/a64instapi.cpp \
|
||||
asmjit/arm/a64instdb.cpp \
|
||||
asmjit/arm/a64operand.cpp \
|
||||
asmjit/arm/a64rapass.cpp \
|
||||
asmjit/arm/armformatter.cpp
|
||||
JITX86 = \
|
||||
asmjit/x86/x86assembler.cpp \
|
||||
asmjit/x86/x86builder.cpp \
|
||||
asmjit/x86/x86compiler.cpp \
|
||||
asmjit/x86/x86emithelper.cpp \
|
||||
asmjit/x86/x86formatter.cpp \
|
||||
asmjit/x86/x86func.cpp \
|
||||
asmjit/x86/x86instapi.cpp \
|
||||
asmjit/x86/x86instdb.cpp \
|
||||
asmjit/x86/x86operand.cpp \
|
||||
asmjit/x86/x86rapass.cpp
|
||||
JITCORE= \
|
||||
asmjit/core/archtraits.cpp \
|
||||
asmjit/core/assembler.cpp \
|
||||
asmjit/core/builder.cpp \
|
||||
asmjit/core/codeholder.cpp \
|
||||
asmjit/core/codewriter.cpp \
|
||||
asmjit/core/compiler.cpp \
|
||||
asmjit/core/constpool.cpp \
|
||||
asmjit/core/cpuinfo.cpp \
|
||||
asmjit/core/emithelper.cpp \
|
||||
asmjit/core/emitter.cpp \
|
||||
asmjit/core/emitterutils.cpp \
|
||||
asmjit/core/environment.cpp \
|
||||
asmjit/core/errorhandler.cpp \
|
||||
asmjit/core/formatter.cpp \
|
||||
asmjit/core/funcargscontext.cpp \
|
||||
asmjit/core/func.cpp \
|
||||
asmjit/core/globals.cpp \
|
||||
asmjit/core/inst.cpp \
|
||||
asmjit/core/jitallocator.cpp \
|
||||
asmjit/core/jitruntime.cpp \
|
||||
asmjit/core/logger.cpp \
|
||||
asmjit/core/operand.cpp \
|
||||
asmjit/core/osutils.cpp \
|
||||
asmjit/core/ralocal.cpp \
|
||||
asmjit/core/rapass.cpp \
|
||||
asmjit/core/rastack.cpp \
|
||||
asmjit/core/string.cpp \
|
||||
asmjit/core/support.cpp \
|
||||
asmjit/core/target.cpp \
|
||||
asmjit/core/type.cpp \
|
||||
asmjit/core/virtmem.cpp \
|
||||
asmjit/core/zone.cpp \
|
||||
asmjit/core/zonehash.cpp \
|
||||
asmjit/core/zonelist.cpp \
|
||||
asmjit/core/zonestack.cpp \
|
||||
asmjit/core/zonetree.cpp \
|
||||
asmjit/core/zonevector.cpp
|
||||
|
||||
JITOBJ=$(JITX86:asmjit/x86/%.cpp=build/x86.%.o) \
|
||||
$(JITARM:asmjit/arm/%.cpp=build/arm.%.o) \
|
||||
$(JIXCORE:asmjit/core/%.cpp=build/core.%.o)
|
||||
|
||||
LEPTON_DIR=.
|
||||
|
||||
include $(LEPTON_DIR)/Settings.mk
|
||||
|
||||
EXTRAMAKE=Makefile.lammps.empty
|
||||
LIB=liblepton.a
|
||||
|
||||
ifeq ($(ENABLE_JIT),1)
|
||||
OBJ += $(JITOBJ)
|
||||
endif
|
||||
|
||||
INC += $(LEPTON_INC)
|
||||
CXXFLAGS += $(LEPTON_DEF)
|
||||
|
||||
all: $(LIB) Makefile.lammps
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
build/lepton.%.o: src/%.cpp build
|
||||
$(CXX) $(INC) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
build/arm.%.o: asmjit/arm/%.cpp build
|
||||
$(CXX) $(INC) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
build/x86.%.o: asmjit/x86/%.cpp build
|
||||
$(CXX) $(INC) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
build/core.%.o: asmjit/core/%.cpp build
|
||||
$(CXX) $(INC) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
Makefile.lammps:
|
||||
cp $(EXTRAMAKE) $@
|
||||
sed -i -e 's,^.*lepton_SYSINC *=.*$$,lepton_SYSINC = $(DEF),' $@
|
||||
|
||||
.PHONY: all lib clean
|
||||
|
||||
$(LIB) : $(OBJ)
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
clean:
|
||||
rm -f build/*.o $(LIB) *~ Makefile.lammps
|
||||
|
||||
|
||||
69
lib/lepton/Install.py
Executable file
69
lib/lepton/Install.py
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Install.py tool to build the Lepton library
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import sys, os, subprocess
|
||||
from argparse import ArgumentParser
|
||||
|
||||
sys.path.append('..')
|
||||
from install_helpers import get_cpus, fullpath
|
||||
|
||||
parser = ArgumentParser(prog='Install.py',
|
||||
description="LAMMPS Lepton library build wrapper script")
|
||||
|
||||
HELP = """
|
||||
Syntax from src dir: make lib-lepton args="-m machine"
|
||||
Syntax from lib dir: python Install.py -m machine
|
||||
|
||||
specify -m
|
||||
|
||||
Examples:
|
||||
|
||||
make lib-lepton args="-m serial" # build Lepton lib with same settings as in the serial Makefile in src
|
||||
python Install.py -m mpi # build Lepton lib with same settings as in the mpi Makefile in src
|
||||
"""
|
||||
|
||||
# parse and process arguments
|
||||
|
||||
parser.add_argument("-m", "--machine",
|
||||
help="suffix of a <libname>/Makefile.* file used for compiling this library")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# print help message and exit, if neither build nor path options are given
|
||||
if not args.machine:
|
||||
parser.print_help()
|
||||
sys.exit(HELP)
|
||||
|
||||
machine = args.machine
|
||||
|
||||
# set lib from working dir
|
||||
|
||||
cwd = fullpath('.')
|
||||
lib = os.path.basename(cwd)
|
||||
|
||||
if not os.path.exists("Makefile.%s" % machine):
|
||||
sys.exit("lib/%s/Makefile.%s does not exist" % (lib, machine))
|
||||
|
||||
# make the library with parallel make
|
||||
n_cpus = get_cpus()
|
||||
|
||||
print("Building lib%s.a ..." % lib)
|
||||
cmd = "make -f Makefile.%s clean; make -f Makefile.%s -j%d" % (machine, machine, n_cpus)
|
||||
try:
|
||||
txt = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||
print(txt.decode('UTF-8'))
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("Make failed with:\n %s" % e.output.decode('UTF-8'))
|
||||
sys.exit(1)
|
||||
|
||||
if os.path.exists("lib%s.a" % lib):
|
||||
print("Build was successful")
|
||||
else:
|
||||
sys.exit("Build of lib/%s/lib%s.a was NOT successful" % (lib, lib))
|
||||
|
||||
if not os.path.exists("Makefile.lammps"):
|
||||
print("WARNING: lib/%s/Makefile.lammps was NOT created" % lib)
|
||||
20
lib/lepton/LICENSE
Normal file
20
lib/lepton/LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
Portions copyright (c) 2009-2019 Stanford University and the Authors.
|
||||
Authors: Peter Eastman and OpenMM contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
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, CONTRIBUTORS OR COPYRIGHT HOLDERS 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.
|
||||
5
lib/lepton/Makefile.lammps.empty
Normal file
5
lib/lepton/Makefile.lammps.empty
Normal file
@ -0,0 +1,5 @@
|
||||
# Settings that the LAMMPS build will import when this package library is used
|
||||
|
||||
lepton_SYSINC =
|
||||
lepton_SYSLIB =
|
||||
lepton_SYSPATH =
|
||||
8
lib/lepton/Makefile.mpi
Normal file
8
lib/lepton/Makefile.mpi
Normal file
@ -0,0 +1,8 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
CC=mpicxx
|
||||
CXXFLAGS=-D_DEFAULT_SOURCE -O2 -Wall -fPIC -std=c++11
|
||||
AR=ar
|
||||
ARFLAGS=rc
|
||||
|
||||
include Common.mk
|
||||
8
lib/lepton/Makefile.serial
Normal file
8
lib/lepton/Makefile.serial
Normal file
@ -0,0 +1,8 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
CC=g++
|
||||
CXXFLAGS=-D_DEFAULT_SOURCE -O3 -DNDEBUG -Wall -fPIC -std=c++11 -ffast-math -msse4.2
|
||||
AR=ar
|
||||
ARFLAGS=rc
|
||||
|
||||
include Common.mk
|
||||
28
lib/lepton/README.md
Normal file
28
lib/lepton/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
This directory contains the lepton library from the OpenMM software
|
||||
which allows to efficiently evaluate mathematical expressions from
|
||||
strings. This library is used with the LEPTON package that support
|
||||
force styles within LAMMPS that make use of this library.
|
||||
|
||||
You can type "make lib-lepton" from the src directory to see help on how
|
||||
to build this library via make commands, or you can do the same thing
|
||||
by typing "python Install.py" from within this directory, or you can
|
||||
do it manually by following the instructions below.
|
||||
|
||||
---------------------
|
||||
|
||||
Lepton (short for “lightweight expression parser”) is a C++ library for
|
||||
parsing, evaluating, differentiating, and analyzing mathematical
|
||||
expressions. It takes expressions in the form of text strings, then
|
||||
converts them to an internal representation suitable for evaluation or
|
||||
analysis. Here are some of its major features:
|
||||
|
||||
- Support for a large number of standard mathematical functions and operations.
|
||||
- Support for user defined custom functions.
|
||||
- A variety of optimizations for automatically simplifying expressions.
|
||||
- Computing analytic derivatives.
|
||||
- Representing parsed expressions in two different forms (tree or program) suitable for
|
||||
further analysis or processing.
|
||||
- Support for just-in-time compilation via asmjit library on x86 (autodetected)
|
||||
This should make evaluation about 2 times faster
|
||||
|
||||
Lepton was originally created for use in the [OpenMM project](https://openmm.org)
|
||||
17
lib/lepton/Settings.mk
Normal file
17
lib/lepton/Settings.mk
Normal file
@ -0,0 +1,17 @@
|
||||
# makefile variables and settings related to configuring JIT with Lepton.
|
||||
|
||||
ENABLE_JIT=0
|
||||
ifeq ($(shell uname -m),x86_64)
|
||||
ENABLE_JIT=1
|
||||
endif
|
||||
ifeq ($(shell uname -m),amd64)
|
||||
ENABLE_JIT=1
|
||||
endif
|
||||
|
||||
LEPTON_INC = -I$(LEPTON_DIR)/include
|
||||
LEPTON_DEF = -DLEPTON_BUILDING_STATIC_LIBRARY=1
|
||||
|
||||
ifeq ($(ENABLE_JIT),1)
|
||||
LEPTON_INC += -I$(LEPTON_DIR)
|
||||
LEPTON_DEF += -DLEPTON_USE_JIT=1 -DASMJIT_BUILD_X86=1 -DASMJIT_STATIC=1 -DASMJIT_BUILD_RELEASE=1
|
||||
endif
|
||||
17
lib/lepton/asmjit/LICENSE.md
Normal file
17
lib/lepton/asmjit/LICENSE.md
Normal file
@ -0,0 +1,17 @@
|
||||
Copyright (c) 2008-2020 The AsmJit Authors
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
62
lib/lepton/asmjit/a64.h
Normal file
62
lib/lepton/asmjit/a64.h
Normal file
@ -0,0 +1,62 @@
|
||||
// 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_A64_H_INCLUDED
|
||||
#define ASMJIT_A64_H_INCLUDED
|
||||
|
||||
//! \addtogroup asmjit_a64
|
||||
//!
|
||||
//! ### Emitters
|
||||
//!
|
||||
//! - \ref a64::Assembler - AArch64 assembler (must read, provides examples).
|
||||
//! - \ref a64::Builder - AArch64 builder.
|
||||
//! - \ref a64::Compiler - AArch64 compiler.
|
||||
//! - \ref a64::Emitter - AArch64 emitter (abstract).
|
||||
//!
|
||||
//! ### Supported Instructions
|
||||
//!
|
||||
//! - Emitters:
|
||||
//! - \ref a64::EmitterExplicitT - Provides all instructions that use explicit
|
||||
//! operands, provides also utility functions. The member functions provided
|
||||
//! are part of all ARM/AArch64 emitters.
|
||||
//!
|
||||
//! - Instruction representation:
|
||||
//! - \ref a64::Inst::Id - instruction identifiers.
|
||||
//!
|
||||
//! ### Register Operands
|
||||
//!
|
||||
//! - \ref arm::Reg - Base class for any AArch32/AArch64 register.
|
||||
//! - \ref arm::Gp - General purpose register:
|
||||
//! - \ref arm::GpW - 32-bit register.
|
||||
//! - \ref arm::GpX - 64-bit register.
|
||||
//! - \ref arm::Vec - Vector (SIMD) register:
|
||||
//! - \ref arm::VecB - 8-bit SIMD register (AArch64 only).
|
||||
//! - \ref arm::VecH - 16-bit SIMD register (AArch64 only).
|
||||
//! - \ref arm::VecS - 32-bit SIMD register.
|
||||
//! - \ref arm::VecD - 64-bit SIMD register.
|
||||
//! - \ref arm::VecV - 128-bit SIMD register.
|
||||
//!
|
||||
//! ### Memory Operands
|
||||
//!
|
||||
//! - \ref arm::Mem - AArch32/AArch64 memory operand that provides support for all ARM addressing features
|
||||
//! including base, index, pre/post increment, and ARM-specific shift addressing and index extending.
|
||||
//!
|
||||
//! ### Other
|
||||
//!
|
||||
//! - \ref arm::Shift - Shift operation and value.
|
||||
//! - \ref a64::Utils - Utilities that can help during code generation for AArch64.
|
||||
|
||||
#include "./arm.h"
|
||||
#include "./arm/a64assembler.h"
|
||||
#include "./arm/a64builder.h"
|
||||
#include "./arm/a64compiler.h"
|
||||
#include "./arm/a64emitter.h"
|
||||
#include "./arm/a64globals.h"
|
||||
#include "./arm/a64instdb.h"
|
||||
#include "./arm/a64operand.h"
|
||||
#include "./arm/a64utils.h"
|
||||
|
||||
#endif // ASMJIT_A64_H_INCLUDED
|
||||
|
||||
62
lib/lepton/asmjit/arm.h
Normal file
62
lib/lepton/asmjit/arm.h
Normal file
@ -0,0 +1,62 @@
|
||||
// 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_ARM_H_INCLUDED
|
||||
#define ASMJIT_ARM_H_INCLUDED
|
||||
|
||||
//! \addtogroup asmjit_arm
|
||||
//!
|
||||
//! ### Namespaces
|
||||
//!
|
||||
//! - \ref arm - arm namespace provides common functionality for both AArch32 and AArch64 backends.
|
||||
//! - \ref a64 - a64 namespace provides support for AArch64 architecture. In addition it includes
|
||||
//! \ref arm namespace, so you can only use a single namespace when targeting AArch64 architecture.
|
||||
//!
|
||||
//! ### Emitters
|
||||
//!
|
||||
//! - AArch64
|
||||
//! - \ref a64::Assembler - AArch64 assembler (must read, provides examples).
|
||||
//! - \ref a64::Builder - AArch64 builder.
|
||||
//! - \ref a64::Compiler - AArch64 compiler.
|
||||
//! - \ref a64::Emitter - AArch64 emitter (abstract).
|
||||
//!
|
||||
//! ### Supported Instructions
|
||||
//!
|
||||
//! - AArch64:
|
||||
//! - Emitters:
|
||||
//! - \ref a64::EmitterExplicitT - Provides all instructions that use explicit operands, provides also
|
||||
//! utility functions. The member functions provided are part of all AArch64 emitters.
|
||||
//! - Instruction representation:
|
||||
//! - \ref a64::Inst::Id - instruction identifiers.
|
||||
//!
|
||||
//! ### Register Operands
|
||||
//!
|
||||
//! - \ref arm::Reg - Base class for any AArch32/AArch64 register.
|
||||
//! - \ref arm::Gp - General purpose register:
|
||||
//! - \ref arm::GpW - 32-bit register.
|
||||
//! - \ref arm::GpX - 64-bit register.
|
||||
//! - \ref arm::Vec - Vector (SIMD) register:
|
||||
//! - \ref arm::VecB - 8-bit SIMD register (AArch64 only).
|
||||
//! - \ref arm::VecH - 16-bit SIMD register (AArch64 only).
|
||||
//! - \ref arm::VecS - 32-bit SIMD register.
|
||||
//! - \ref arm::VecD - 64-bit SIMD register.
|
||||
//! - \ref arm::VecV - 128-bit SIMD register.
|
||||
//!
|
||||
//! ### Memory Operands
|
||||
//!
|
||||
//! - \ref arm::Mem - AArch32/AArch64 memory operand that provides support for all ARM addressing features
|
||||
//! including base, index, pre/post increment, and ARM-specific shift addressing and index extending.
|
||||
//!
|
||||
//! ### Other
|
||||
//!
|
||||
//! - \ref arm::Shift - Shift operation and value (both AArch32 and AArch64).
|
||||
//! - \ref arm::DataType - Data type that is part of an instruction in AArch32 mode.
|
||||
//! - \ref a64::Utils - Utilities that can help during code generation for AArch64.
|
||||
|
||||
#include "./core.h"
|
||||
#include "./arm/armglobals.h"
|
||||
#include "./arm/armoperand.h"
|
||||
|
||||
#endif // ASMJIT_ARM_H_INCLUDED
|
||||
81
lib/lepton/asmjit/arm/a64archtraits_p.h
Normal file
81
lib/lepton/asmjit/arm/a64archtraits_p.h
Normal file
@ -0,0 +1,81 @@
|
||||
// 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_ARM_A64ARCHTRAITS_P_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64ARCHTRAITS_P_H_INCLUDED
|
||||
|
||||
#include "../core/archtraits.h"
|
||||
#include "../core/misc_p.h"
|
||||
#include "../core/type.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
static const constexpr ArchTraits a64ArchTraits = {
|
||||
// SP/FP/LR/PC.
|
||||
Gp::kIdSp, Gp::kIdFp, Gp::kIdLr, 0xFF,
|
||||
|
||||
// Reserved.
|
||||
{ 0, 0, 0 },
|
||||
|
||||
// HW stack alignment (AArch64 requires stack aligned to 64 bytes).
|
||||
16,
|
||||
|
||||
// Min/max stack offset - byte addressing is the worst, VecQ addressing the best.
|
||||
4095, 65520,
|
||||
|
||||
// Instruction hints [Gp, Vec, ExtraVirt2, ExtraVirt3].
|
||||
{{
|
||||
InstHints::kPushPop,
|
||||
InstHints::kPushPop,
|
||||
InstHints::kNoHints,
|
||||
InstHints::kNoHints
|
||||
}},
|
||||
|
||||
// RegInfo.
|
||||
#define V(index) OperandSignature{arm::RegTraits<RegType(index)>::kSignature}
|
||||
{{ ASMJIT_LOOKUP_TABLE_32(V, 0) }},
|
||||
#undef V
|
||||
|
||||
// RegTypeToTypeId.
|
||||
#define V(index) TypeId(arm::RegTraits<RegType(index)>::kTypeId)
|
||||
{{ ASMJIT_LOOKUP_TABLE_32(V, 0) }},
|
||||
#undef V
|
||||
|
||||
// TypeIdToRegType.
|
||||
#define V(index) (index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kInt8) ? RegType::kARM_GpW : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kUInt8) ? RegType::kARM_GpW : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kInt16) ? RegType::kARM_GpW : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kUInt16) ? RegType::kARM_GpW : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kInt32) ? RegType::kARM_GpW : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kUInt32) ? RegType::kARM_GpW : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kInt64) ? RegType::kARM_GpX : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kUInt64) ? RegType::kARM_GpX : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kIntPtr) ? RegType::kARM_GpX : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kUIntPtr) ? RegType::kARM_GpX : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kFloat32) ? RegType::kARM_VecS : \
|
||||
index + uint32_t(TypeId::_kBaseStart) == uint32_t(TypeId::kFloat64) ? RegType::kARM_VecD : RegType::kNone)
|
||||
{{ ASMJIT_LOOKUP_TABLE_32(V, 0) }},
|
||||
#undef V
|
||||
|
||||
// Word names of 8-bit, 16-bit, 32-bit, and 64-bit quantities.
|
||||
{
|
||||
ArchTypeNameId::kByte,
|
||||
ArchTypeNameId::kHWord,
|
||||
ArchTypeNameId::kWord,
|
||||
ArchTypeNameId::kXWord
|
||||
}
|
||||
};
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_A64ARCHTRAITS_P_H_INCLUDED
|
||||
5115
lib/lepton/asmjit/arm/a64assembler.cpp
Normal file
5115
lib/lepton/asmjit/arm/a64assembler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
72
lib/lepton/asmjit/arm/a64assembler.h
Normal file
72
lib/lepton/asmjit/arm/a64assembler.h
Normal file
@ -0,0 +1,72 @@
|
||||
// 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_ARM_A64ASSEMBLER_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64ASSEMBLER_H_INCLUDED
|
||||
|
||||
#include "../core/assembler.h"
|
||||
#include "../arm/a64emitter.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
//! AArch64 assembler implementation.
|
||||
class ASMJIT_VIRTAPI Assembler
|
||||
: public BaseAssembler,
|
||||
public EmitterExplicitT<Assembler> {
|
||||
|
||||
public:
|
||||
typedef BaseAssembler Base;
|
||||
|
||||
//! \name Construction / Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Assembler(CodeHolder* code = nullptr) noexcept;
|
||||
ASMJIT_API virtual ~Assembler() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Gets whether the current ARM mode is THUMB (alternative to 32-bit ARM encoding).
|
||||
inline bool isInThumbMode() const noexcept { return _environment.isArchThumb(); }
|
||||
|
||||
//! Gets the current code alignment of the current mode (ARM vs THUMB).
|
||||
inline uint32_t codeAlignment() const noexcept { return isInThumbMode() ? 2 : 4; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Emit
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error _emit(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Align
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error align(AlignMode alignMode, uint32_t alignment) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
|
||||
ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_A64ASSEMBLER_H_INCLUDED
|
||||
51
lib/lepton/asmjit/arm/a64builder.cpp
Normal file
51
lib/lepton/asmjit/arm/a64builder.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
// 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"
|
||||
#if !defined(ASMJIT_NO_AARCH64) && !defined(ASMJIT_NO_BUILDER)
|
||||
|
||||
#include "../arm/a64assembler.h"
|
||||
#include "../arm/a64builder.h"
|
||||
#include "../arm/a64emithelper_p.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
// a64::Builder - Construction & Destruction
|
||||
// =========================================
|
||||
|
||||
Builder::Builder(CodeHolder* code) noexcept : BaseBuilder() {
|
||||
_archMask = uint64_t(1) << uint32_t(Arch::kAArch64);
|
||||
assignEmitterFuncs(this);
|
||||
|
||||
if (code)
|
||||
code->attach(this);
|
||||
}
|
||||
Builder::~Builder() noexcept {}
|
||||
|
||||
// a64::Builder - Events
|
||||
// =====================
|
||||
|
||||
Error Builder::onAttach(CodeHolder* code) noexcept {
|
||||
return Base::onAttach(code);
|
||||
}
|
||||
|
||||
Error Builder::onDetach(CodeHolder* code) noexcept {
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
// a64::Builder - Finalize
|
||||
// =======================
|
||||
|
||||
Error Builder::finalize() {
|
||||
ASMJIT_PROPAGATE(runPasses());
|
||||
Assembler a(_code);
|
||||
a.addEncodingOptions(encodingOptions());
|
||||
a.addDiagnosticOptions(diagnosticOptions());
|
||||
return serializeTo(&a);
|
||||
}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_AARCH64 && !ASMJIT_NO_BUILDER
|
||||
57
lib/lepton/asmjit/arm/a64builder.h
Normal file
57
lib/lepton/asmjit/arm/a64builder.h
Normal file
@ -0,0 +1,57 @@
|
||||
// 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_ARM_A64BUILDER_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64BUILDER_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
|
||||
#include "../core/builder.h"
|
||||
#include "../arm/a64emitter.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
//! AArch64 builder implementation.
|
||||
class ASMJIT_VIRTAPI Builder
|
||||
: public BaseBuilder,
|
||||
public EmitterExplicitT<Builder> {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(Builder)
|
||||
typedef BaseBuilder Base;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API explicit Builder(CodeHolder* code = nullptr) noexcept;
|
||||
ASMJIT_API virtual ~Builder() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
|
||||
ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Finalize
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error finalize() override;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_BUILDER
|
||||
#endif // ASMJIT_ARM_A64BUILDER_H_INCLUDED
|
||||
60
lib/lepton/asmjit/arm/a64compiler.cpp
Normal file
60
lib/lepton/asmjit/arm/a64compiler.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
// 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"
|
||||
#if !defined(ASMJIT_NO_AARCH64) && !defined(ASMJIT_NO_COMPILER)
|
||||
|
||||
#include "../arm/a64assembler.h"
|
||||
#include "../arm/a64compiler.h"
|
||||
#include "../arm/a64emithelper_p.h"
|
||||
#include "../arm/a64rapass_p.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
// a64::Compiler - Construction & Destruction
|
||||
// ==========================================
|
||||
|
||||
Compiler::Compiler(CodeHolder* code) noexcept : BaseCompiler() {
|
||||
_archMask = uint64_t(1) << uint32_t(Arch::kAArch64);
|
||||
assignEmitterFuncs(this);
|
||||
|
||||
if (code)
|
||||
code->attach(this);
|
||||
}
|
||||
Compiler::~Compiler() noexcept {}
|
||||
|
||||
// a64::Compiler - Events
|
||||
// ======================
|
||||
|
||||
Error Compiler::onAttach(CodeHolder* code) noexcept {
|
||||
ASMJIT_PROPAGATE(Base::onAttach(code));
|
||||
Error err = addPassT<ARMRAPass>();
|
||||
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
onDetach(code);
|
||||
return err;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error Compiler::onDetach(CodeHolder* code) noexcept {
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
// a64::Compiler - Finalize
|
||||
// ========================
|
||||
|
||||
Error Compiler::finalize() {
|
||||
ASMJIT_PROPAGATE(runPasses());
|
||||
Assembler a(_code);
|
||||
a.addEncodingOptions(encodingOptions());
|
||||
a.addDiagnosticOptions(diagnosticOptions());
|
||||
return serializeTo(&a);
|
||||
}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_AARCH64 && !ASMJIT_NO_COMPILER
|
||||
247
lib/lepton/asmjit/arm/a64compiler.h
Normal file
247
lib/lepton/asmjit/arm/a64compiler.h
Normal file
@ -0,0 +1,247 @@
|
||||
// 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_ARM_ARMCOMPILER_H_INCLUDED
|
||||
#define ASMJIT_ARM_ARMCOMPILER_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
|
||||
#include "../core/compiler.h"
|
||||
#include "../core/type.h"
|
||||
#include "../arm/a64emitter.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
//! AArch64 compiler implementation.
|
||||
class ASMJIT_VIRTAPI Compiler
|
||||
: public BaseCompiler,
|
||||
public EmitterExplicitT<Compiler> {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(Compiler)
|
||||
typedef BaseCompiler Base;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API explicit Compiler(CodeHolder* code = nullptr) noexcept;
|
||||
ASMJIT_API virtual ~Compiler() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Virtual Registers
|
||||
//! \{
|
||||
|
||||
//! \cond INTERNAL
|
||||
template<typename RegT, typename Type>
|
||||
inline RegT _newRegInternal(const Type& type) {
|
||||
RegT reg(Globals::NoInit);
|
||||
_newReg(®, type, nullptr);
|
||||
return reg;
|
||||
}
|
||||
|
||||
template<typename RegT, typename Type, typename... Args>
|
||||
inline RegT _newRegInternal(const Type& type, const char* s, Args&&... args) {
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
RegT reg(Globals::NoInit);
|
||||
if (sizeof...(Args) == 0)
|
||||
_newReg(®, type, s);
|
||||
else
|
||||
_newRegFmt(®, type, s, std::forward<Args>(args)...);
|
||||
return reg;
|
||||
#else
|
||||
DebugUtils::unused(std::forward<Args>(args)...);
|
||||
RegT reg(Globals::NoInit);
|
||||
_newReg(®, type, nullptr);
|
||||
return reg;
|
||||
#endif
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
template<typename RegT, typename... Args>
|
||||
inline RegT newSimilarReg(const RegT& ref, Args&&... args) {
|
||||
return _newRegInternal<RegT>(ref, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline Reg newReg(TypeId typeId, Args&&... args) { return _newRegInternal<Reg>(typeId, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Gp newGp(TypeId typeId, Args&&... args) { return _newRegInternal<Gp>(typeId, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Vec newVec(TypeId typeId, Args&&... args) { return _newRegInternal<Vec>(typeId, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Gp newInt32(Args&&... args) { return _newRegInternal<Gp>(TypeId::kInt32, std::forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
inline Gp newUInt32(Args&&... args) { return _newRegInternal<Gp>(TypeId::kUInt32, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Gp newInt64(Args&&... args) { return _newRegInternal<Gp>(TypeId::kInt64, std::forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
inline Gp newUInt64(Args&&... args) { return _newRegInternal<Gp>(TypeId::kUInt64, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Gp newIntPtr(Args&&... args) { return _newRegInternal<Gp>(TypeId::kIntPtr, std::forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
inline Gp newUIntPtr(Args&&... args) { return _newRegInternal<Gp>(TypeId::kUIntPtr, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Gp newGpw(Args&&... args) { return _newRegInternal<Gp>(TypeId::kUInt32, std::forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
inline Gp newGpx(Args&&... args) { return _newRegInternal<Gp>(TypeId::kUInt64, std::forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
inline Gp newGpz(Args&&... args) { return _newRegInternal<Gp>(TypeId::kUIntPtr, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Vec newVecS(Args&&... args) { return _newRegInternal<Vec>(TypeId::kFloat32, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Vec newVecD(Args&&... args) { return _newRegInternal<Vec>(TypeId::kFloat64, std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
inline Vec newVecQ(Args&&... args) { return _newRegInternal<Vec>(TypeId::kUInt8x16, std::forward<Args>(args)...); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Stack
|
||||
//! \{
|
||||
|
||||
//! Creates a new memory chunk allocated on the current function's stack.
|
||||
inline Mem newStack(uint32_t size, uint32_t alignment, const char* name = nullptr) {
|
||||
Mem m(Globals::NoInit);
|
||||
_newStack(&m, size, alignment, name);
|
||||
return m;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Constants
|
||||
//! \{
|
||||
|
||||
//! Put data to a constant-pool and get a memory reference to it.
|
||||
inline Mem newConst(ConstPoolScope scope, const void* data, size_t size) {
|
||||
Mem m(Globals::NoInit);
|
||||
_newConst(&m, scope, data, size);
|
||||
return m;
|
||||
}
|
||||
|
||||
//! Put a BYTE `val` to a constant-pool (8 bits).
|
||||
inline Mem newByteConst(ConstPoolScope scope, uint8_t val) noexcept { return newConst(scope, &val, 1); }
|
||||
//! Put a HWORD `val` to a constant-pool (16 bits).
|
||||
inline Mem newHWordConst(ConstPoolScope scope, uint16_t val) noexcept { return newConst(scope, &val, 2); }
|
||||
//! Put a WORD `val` to a constant-pool (32 bits).
|
||||
inline Mem newWordConst(ConstPoolScope scope, uint32_t val) noexcept { return newConst(scope, &val, 4); }
|
||||
//! Put a DWORD `val` to a constant-pool (64 bits).
|
||||
inline Mem newDWordConst(ConstPoolScope scope, uint64_t val) noexcept { return newConst(scope, &val, 8); }
|
||||
|
||||
//! Put a WORD `val` to a constant-pool.
|
||||
inline Mem newInt16Const(ConstPoolScope scope, int16_t val) noexcept { return newConst(scope, &val, 2); }
|
||||
//! Put a WORD `val` to a constant-pool.
|
||||
inline Mem newUInt16Const(ConstPoolScope scope, uint16_t val) noexcept { return newConst(scope, &val, 2); }
|
||||
//! Put a DWORD `val` to a constant-pool.
|
||||
inline Mem newInt32Const(ConstPoolScope scope, int32_t val) noexcept { return newConst(scope, &val, 4); }
|
||||
//! Put a DWORD `val` to a constant-pool.
|
||||
inline Mem newUInt32Const(ConstPoolScope scope, uint32_t val) noexcept { return newConst(scope, &val, 4); }
|
||||
//! Put a QWORD `val` to a constant-pool.
|
||||
inline Mem newInt64Const(ConstPoolScope scope, int64_t val) noexcept { return newConst(scope, &val, 8); }
|
||||
//! Put a QWORD `val` to a constant-pool.
|
||||
inline Mem newUInt64Const(ConstPoolScope scope, uint64_t val) noexcept { return newConst(scope, &val, 8); }
|
||||
|
||||
//! Put a SP-FP `val` to a constant-pool.
|
||||
inline Mem newFloatConst(ConstPoolScope scope, float val) noexcept { return newConst(scope, &val, 4); }
|
||||
//! Put a DP-FP `val` to a constant-pool.
|
||||
inline Mem newDoubleConst(ConstPoolScope scope, double val) noexcept { return newConst(scope, &val, 8); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Instruction Options
|
||||
//! \{
|
||||
|
||||
//! Force the compiler to not follow the conditional or unconditional jump.
|
||||
inline Compiler& unfollow() noexcept { _instOptions |= InstOptions::kUnfollow; return *this; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Compiler specific
|
||||
//! \{
|
||||
|
||||
//! Special pseudo-instruction that can be used to load a memory address into `o0` GP register.
|
||||
//!
|
||||
//! \note At the moment this instruction is only useful to load a stack allocated address into a GP register
|
||||
//! for further use. It makes very little sense to use it for anything else. The semantics of this instruction
|
||||
//! is the same as X86 `LEA` (load effective address) instruction.
|
||||
inline Error loadAddressOf(const Gp& o0, const Mem& o1) { return _emitter()->_emitI(Inst::kIdAdr, o0, o1); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Function Call & Ret Intrinsics
|
||||
//! \{
|
||||
|
||||
//! Invoke a function call without `target` type enforcement.
|
||||
inline Error invoke_(InvokeNode** out, const Operand_& target, const FuncSignature& signature) {
|
||||
return addInvokeNode(out, Inst::kIdBlr, target, signature);
|
||||
}
|
||||
|
||||
//! Invoke a function call of the given `target` and `signature` and store the added node to `out`.
|
||||
//!
|
||||
//! Creates a new \ref InvokeNode, initializes all the necessary members to match the given function `signature`,
|
||||
//! adds the node to the compiler, and stores its pointer to `out`. The operation is atomic, if anything fails
|
||||
//! nullptr is stored in `out` and error code is returned.
|
||||
inline Error invoke(InvokeNode** out, const Gp& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline Error invoke(InvokeNode** out, const Mem& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline Error invoke(InvokeNode** out, const Label& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline Error invoke(InvokeNode** out, const Imm& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline Error invoke(InvokeNode** out, uint64_t target, const FuncSignature& signature) { return invoke_(out, Imm(int64_t(target)), signature); }
|
||||
|
||||
//! Return.
|
||||
inline Error ret() { return addRet(Operand(), Operand()); }
|
||||
//! \overload
|
||||
inline Error ret(const BaseReg& o0) { return addRet(o0, Operand()); }
|
||||
//! \overload
|
||||
inline Error ret(const BaseReg& o0, const BaseReg& o1) { return addRet(o0, o1); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Jump Tables Support
|
||||
//! \{
|
||||
|
||||
using EmitterExplicitT<Compiler>::br;
|
||||
|
||||
//! Adds a jump to the given `target` with the provided jump `annotation`.
|
||||
inline Error br(const BaseReg& target, JumpAnnotation* annotation) { return emitAnnotatedJump(Inst::kIdBr, target, annotation); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
|
||||
ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Finalize
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error finalize() override;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_COMPILER
|
||||
#endif // ASMJIT_ARM_ARMCOMPILER_H_INCLUDED
|
||||
464
lib/lepton/asmjit/arm/a64emithelper.cpp
Normal file
464
lib/lepton/asmjit/arm/a64emithelper.cpp
Normal file
@ -0,0 +1,464 @@
|
||||
// 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"
|
||||
#if !defined(ASMJIT_NO_AARCH64)
|
||||
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/funcargscontext_p.h"
|
||||
#include "../core/string.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
#include "../arm/a64emithelper_p.h"
|
||||
#include "../arm/a64formatter_p.h"
|
||||
#include "../arm/a64instapi_p.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
// a64::EmitHelper - Emit Operations
|
||||
// =================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error EmitHelper::emitRegMove(
|
||||
const Operand_& dst_,
|
||||
const Operand_& src_, TypeId typeId, const char* comment) {
|
||||
|
||||
Emitter* emitter = _emitter->as<Emitter>();
|
||||
|
||||
// Invalid or abstract TypeIds are not allowed.
|
||||
ASMJIT_ASSERT(TypeUtils::isValid(typeId) && !TypeUtils::isAbstract(typeId));
|
||||
|
||||
emitter->setInlineComment(comment);
|
||||
|
||||
if (dst_.isReg() && src_.isMem()) {
|
||||
Reg dst(dst_.as<Reg>());
|
||||
Mem src(src_.as<Mem>());
|
||||
|
||||
switch (typeId) {
|
||||
case TypeId::kInt8:
|
||||
case TypeId::kUInt8:
|
||||
return emitter->ldrb(dst.as<Gp>(), src);
|
||||
|
||||
case TypeId::kInt16:
|
||||
case TypeId::kUInt16:
|
||||
return emitter->ldrh(dst.as<Gp>(), src);
|
||||
|
||||
case TypeId::kInt32:
|
||||
case TypeId::kUInt32:
|
||||
return emitter->ldr(dst.as<Gp>().w(), src);
|
||||
|
||||
case TypeId::kInt64:
|
||||
case TypeId::kUInt64:
|
||||
return emitter->ldr(dst.as<Gp>().x(), src);
|
||||
|
||||
default: {
|
||||
if (TypeUtils::isFloat32(typeId) || TypeUtils::isVec32(typeId))
|
||||
return emitter->ldr(dst.as<Vec>().s(), src);
|
||||
|
||||
if (TypeUtils::isFloat64(typeId) || TypeUtils::isVec64(typeId))
|
||||
return emitter->ldr(dst.as<Vec>().d(), src);
|
||||
|
||||
if (TypeUtils::isVec128(typeId))
|
||||
return emitter->ldr(dst.as<Vec>().q(), src);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dst_.isMem() && src_.isReg()) {
|
||||
Mem dst(dst_.as<Mem>());
|
||||
Reg src(src_.as<Reg>());
|
||||
|
||||
switch (typeId) {
|
||||
case TypeId::kInt8:
|
||||
case TypeId::kUInt8:
|
||||
return emitter->strb(src.as<Gp>(), dst);
|
||||
|
||||
case TypeId::kInt16:
|
||||
case TypeId::kUInt16:
|
||||
return emitter->strh(src.as<Gp>(), dst);
|
||||
|
||||
case TypeId::kInt32:
|
||||
case TypeId::kUInt32:
|
||||
return emitter->str(src.as<Gp>().w(), dst);
|
||||
|
||||
case TypeId::kInt64:
|
||||
case TypeId::kUInt64:
|
||||
return emitter->str(src.as<Gp>().x(), dst);
|
||||
|
||||
default: {
|
||||
if (TypeUtils::isFloat32(typeId) || TypeUtils::isVec32(typeId))
|
||||
return emitter->str(src.as<Vec>().s(), dst);
|
||||
|
||||
if (TypeUtils::isFloat64(typeId) || TypeUtils::isVec64(typeId))
|
||||
return emitter->str(src.as<Vec>().d(), dst);
|
||||
|
||||
if (TypeUtils::isVec128(typeId))
|
||||
return emitter->str(src.as<Vec>().q(), dst);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dst_.isReg() && src_.isReg()) {
|
||||
Reg dst(dst_.as<Reg>());
|
||||
Reg src(src_.as<Reg>());
|
||||
|
||||
switch (typeId) {
|
||||
case TypeId::kInt8:
|
||||
case TypeId::kUInt8:
|
||||
case TypeId::kInt16:
|
||||
case TypeId::kUInt16:
|
||||
case TypeId::kInt32:
|
||||
case TypeId::kUInt32:
|
||||
case TypeId::kInt64:
|
||||
case TypeId::kUInt64:
|
||||
return emitter->mov(dst.as<Gp>().x(), src.as<Gp>().x());
|
||||
|
||||
default: {
|
||||
if (TypeUtils::isFloat32(typeId) || TypeUtils::isVec32(typeId))
|
||||
return emitter->fmov(dst.as<Vec>().s(), src.as<Vec>().s());
|
||||
|
||||
if (TypeUtils::isFloat64(typeId) || TypeUtils::isVec64(typeId))
|
||||
return emitter->mov(dst.as<Vec>().b8(), src.as<Vec>().b8());
|
||||
|
||||
if (TypeUtils::isVec128(typeId))
|
||||
return emitter->mov(dst.as<Vec>().b16(), src.as<Vec>().b16());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitter->setInlineComment(nullptr);
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
Error EmitHelper::emitRegSwap(
|
||||
const BaseReg& a,
|
||||
const BaseReg& b, const char* comment) {
|
||||
|
||||
DebugUtils::unused(a, b, comment);
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
// TODO: [ARM] EmitArgMove is unfinished.
|
||||
Error EmitHelper::emitArgMove(
|
||||
const BaseReg& dst_, TypeId dstTypeId,
|
||||
const Operand_& src_, TypeId srcTypeId, const char* comment) {
|
||||
|
||||
// Deduce optional `dstTypeId`, which may be `TypeId::kVoid` in some cases.
|
||||
if (dstTypeId == TypeId::kVoid) {
|
||||
const ArchTraits& archTraits = ArchTraits::byArch(_emitter->arch());
|
||||
dstTypeId = archTraits.regTypeToTypeId(dst_.type());
|
||||
}
|
||||
|
||||
// Invalid or abstract TypeIds are not allowed.
|
||||
ASMJIT_ASSERT(TypeUtils::isValid(dstTypeId) && !TypeUtils::isAbstract(dstTypeId));
|
||||
ASMJIT_ASSERT(TypeUtils::isValid(srcTypeId) && !TypeUtils::isAbstract(srcTypeId));
|
||||
|
||||
Reg dst(dst_.as<Reg>());
|
||||
Operand src(src_);
|
||||
|
||||
uint32_t dstSize = TypeUtils::sizeOf(dstTypeId);
|
||||
uint32_t srcSize = TypeUtils::sizeOf(srcTypeId);
|
||||
|
||||
if (TypeUtils::isInt(dstTypeId)) {
|
||||
if (TypeUtils::isInt(srcTypeId)) {
|
||||
uint32_t x = dstSize == 8;
|
||||
|
||||
dst.setSignature(OperandSignature{x ? uint32_t(GpX::kSignature) : uint32_t(GpW::kSignature)});
|
||||
_emitter->setInlineComment(comment);
|
||||
|
||||
if (src.isReg()) {
|
||||
src.setSignature(dst.signature());
|
||||
return _emitter->emit(Inst::kIdMov, dst, src);
|
||||
}
|
||||
else if (src.isMem()) {
|
||||
InstId instId = Inst::kIdNone;
|
||||
switch (srcTypeId) {
|
||||
case TypeId::kInt8: instId = Inst::kIdLdrsb; break;
|
||||
case TypeId::kUInt8: instId = Inst::kIdLdrb; break;
|
||||
case TypeId::kInt16: instId = Inst::kIdLdrsh; break;
|
||||
case TypeId::kUInt16: instId = Inst::kIdLdrh; break;
|
||||
case TypeId::kInt32: instId = x ? Inst::kIdLdrsw : Inst::kIdLdr; break;
|
||||
case TypeId::kUInt32: instId = Inst::kIdLdr; x = 0; break;
|
||||
case TypeId::kInt64: instId = Inst::kIdLdr; break;
|
||||
case TypeId::kUInt64: instId = Inst::kIdLdr; break;
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
return _emitter->emit(instId, dst, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TypeUtils::isFloat(dstTypeId) || TypeUtils::isVec(dstTypeId)) {
|
||||
if (TypeUtils::isFloat(srcTypeId) || TypeUtils::isVec(srcTypeId)) {
|
||||
switch (srcSize) {
|
||||
case 2: dst.as<Vec>().setSignature(OperandSignature{VecH::kSignature}); break;
|
||||
case 4: dst.as<Vec>().setSignature(OperandSignature{VecS::kSignature}); break;
|
||||
case 8: dst.as<Vec>().setSignature(OperandSignature{VecD::kSignature}); break;
|
||||
case 16: dst.as<Vec>().setSignature(OperandSignature{VecV::kSignature}); break;
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
_emitter->setInlineComment(comment);
|
||||
|
||||
if (src.isReg()) {
|
||||
InstId instId = srcSize <= 4 ? Inst::kIdFmov_v : Inst::kIdMov_v;
|
||||
src.setSignature(dst.signature());
|
||||
return _emitter->emit(instId, dst, src);
|
||||
}
|
||||
else if (src.isMem()) {
|
||||
return _emitter->emit(Inst::kIdLdr_v, dst, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
// a64::EmitHelper - Emit Prolog & Epilog
|
||||
// ======================================
|
||||
|
||||
struct LoadStoreInstructions {
|
||||
InstId singleInstId;
|
||||
InstId pairInstId;
|
||||
};
|
||||
|
||||
struct PrologEpilogInfo {
|
||||
struct RegPair {
|
||||
uint8_t ids[2];
|
||||
uint16_t offset;
|
||||
};
|
||||
|
||||
struct GroupData {
|
||||
RegPair pairs[16];
|
||||
uint32_t pairCount;
|
||||
};
|
||||
|
||||
Support::Array<GroupData, 2> groups;
|
||||
uint32_t sizeTotal;
|
||||
|
||||
Error init(const FuncFrame& frame) noexcept {
|
||||
uint32_t offset = 0;
|
||||
|
||||
for (RegGroup group : Support::EnumValues<RegGroup, RegGroup::kGp, RegGroup::kVec>{}) {
|
||||
GroupData& data = groups[group];
|
||||
|
||||
uint32_t n = 0;
|
||||
uint32_t pairCount = 0;
|
||||
RegPair* pairs = data.pairs;
|
||||
|
||||
uint32_t slotSize = frame.saveRestoreRegSize(group);
|
||||
uint32_t savedRegs = frame.savedRegs(group);
|
||||
|
||||
if (group == RegGroup::kGp && frame.hasPreservedFP()) {
|
||||
// Must be at the beginning of the push/pop sequence.
|
||||
ASMJIT_ASSERT(pairCount == 0);
|
||||
|
||||
pairs[0].offset = uint16_t(offset);
|
||||
pairs[0].ids[0] = Gp::kIdFp;
|
||||
pairs[0].ids[1] = Gp::kIdLr;
|
||||
offset += slotSize * 2;
|
||||
pairCount++;
|
||||
|
||||
savedRegs &= ~Support::bitMask(Gp::kIdFp, Gp::kIdLr);
|
||||
}
|
||||
|
||||
Support::BitWordIterator<uint32_t> it(savedRegs);
|
||||
while (it.hasNext()) {
|
||||
pairs[pairCount].ids[n] = uint8_t(it.next());
|
||||
|
||||
if (++n == 2) {
|
||||
pairs[pairCount].offset = uint16_t(offset);
|
||||
offset += slotSize * 2;
|
||||
|
||||
n = 0;
|
||||
pairCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
pairs[pairCount].ids[1] = uint8_t(BaseReg::kIdBad);
|
||||
pairs[pairCount].offset = uint16_t(offset);
|
||||
offset += slotSize * 2;
|
||||
pairCount++;
|
||||
}
|
||||
|
||||
data.pairCount = pairCount;
|
||||
}
|
||||
|
||||
sizeTotal = offset;
|
||||
return kErrorOk;
|
||||
}
|
||||
};
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) {
|
||||
Emitter* emitter = _emitter->as<Emitter>();
|
||||
|
||||
PrologEpilogInfo pei;
|
||||
ASMJIT_PROPAGATE(pei.init(frame));
|
||||
|
||||
static const Support::Array<Reg, 2> groupRegs = {{ x0, d0 }};
|
||||
static const Support::Array<LoadStoreInstructions, 2> groupInsts = {{
|
||||
{ Inst::kIdStr , Inst::kIdStp },
|
||||
{ Inst::kIdStr_v, Inst::kIdStp_v }
|
||||
}};
|
||||
|
||||
uint32_t adjustInitialOffset = pei.sizeTotal;
|
||||
|
||||
for (RegGroup group : Support::EnumValues<RegGroup, RegGroup::kGp, RegGroup::kVec>{}) {
|
||||
const PrologEpilogInfo::GroupData& data = pei.groups[group];
|
||||
uint32_t pairCount = data.pairCount;
|
||||
|
||||
Reg regs[2] = { groupRegs[group], groupRegs[group] };
|
||||
Mem mem = ptr(sp);
|
||||
|
||||
const LoadStoreInstructions& insts = groupInsts[group];
|
||||
for (uint32_t i = 0; i < pairCount; i++) {
|
||||
const PrologEpilogInfo::RegPair& pair = data.pairs[i];
|
||||
|
||||
regs[0].setId(pair.ids[0]);
|
||||
regs[1].setId(pair.ids[1]);
|
||||
mem.setOffsetLo32(pair.offset);
|
||||
|
||||
if (pair.offset == 0 && adjustInitialOffset) {
|
||||
mem.setOffset(-int(adjustInitialOffset));
|
||||
mem.makePreIndex();
|
||||
}
|
||||
|
||||
if (pair.ids[1] == BaseReg::kIdBad)
|
||||
ASMJIT_PROPAGATE(emitter->emit(insts.singleInstId, regs[0], mem));
|
||||
else
|
||||
ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem));
|
||||
|
||||
mem.resetToFixedOffset();
|
||||
|
||||
if (i == 0 && frame.hasPreservedFP()) {
|
||||
ASMJIT_PROPAGATE(emitter->mov(x29, sp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (frame.hasStackAdjustment()) {
|
||||
uint32_t adj = frame.stackAdjustment();
|
||||
if (adj <= 0xFFFu) {
|
||||
ASMJIT_PROPAGATE(emitter->sub(sp, sp, adj));
|
||||
}
|
||||
else if (adj <= 0xFFFFFFu) {
|
||||
// TODO: [ARM] Prolog - we must touch the pages otherwise it's undefined.
|
||||
ASMJIT_PROPAGATE(emitter->sub(sp, sp, adj & 0x000FFFu));
|
||||
ASMJIT_PROPAGATE(emitter->sub(sp, sp, adj & 0xFFF000u));
|
||||
}
|
||||
else {
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// TODO: [ARM] Emit epilog.
|
||||
ASMJIT_FAVOR_SIZE Error EmitHelper::emitEpilog(const FuncFrame& frame) {
|
||||
Emitter* emitter = _emitter->as<Emitter>();
|
||||
|
||||
PrologEpilogInfo pei;
|
||||
ASMJIT_PROPAGATE(pei.init(frame));
|
||||
|
||||
static const Support::Array<Reg, 2> groupRegs = {{ x0, d0 }};
|
||||
static const Support::Array<LoadStoreInstructions, 2> groupInsts = {{
|
||||
{ Inst::kIdLdr , Inst::kIdLdp },
|
||||
{ Inst::kIdLdr_v, Inst::kIdLdp_v }
|
||||
}};
|
||||
|
||||
uint32_t adjustInitialOffset = pei.sizeTotal;
|
||||
|
||||
if (frame.hasStackAdjustment()) {
|
||||
uint32_t adj = frame.stackAdjustment();
|
||||
if (adj <= 0xFFFu) {
|
||||
ASMJIT_PROPAGATE(emitter->add(sp, sp, adj));
|
||||
}
|
||||
else if (adj <= 0xFFFFFFu) {
|
||||
ASMJIT_PROPAGATE(emitter->add(sp, sp, adj & 0x000FFFu));
|
||||
ASMJIT_PROPAGATE(emitter->add(sp, sp, adj & 0xFFF000u));
|
||||
}
|
||||
else {
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
}
|
||||
|
||||
for (int g = 1; g >= 0; g--) {
|
||||
RegGroup group = RegGroup(g);
|
||||
const PrologEpilogInfo::GroupData& data = pei.groups[group];
|
||||
uint32_t pairCount = data.pairCount;
|
||||
|
||||
Reg regs[2] = { groupRegs[group], groupRegs[group] };
|
||||
Mem mem = ptr(sp);
|
||||
|
||||
const LoadStoreInstructions& insts = groupInsts[group];
|
||||
|
||||
for (int i = int(pairCount) - 1; i >= 0; i--) {
|
||||
const PrologEpilogInfo::RegPair& pair = data.pairs[i];
|
||||
|
||||
regs[0].setId(pair.ids[0]);
|
||||
regs[1].setId(pair.ids[1]);
|
||||
mem.setOffsetLo32(pair.offset);
|
||||
|
||||
if (pair.offset == 0 && adjustInitialOffset) {
|
||||
mem.setOffset(int(adjustInitialOffset));
|
||||
mem.makePostIndex();
|
||||
}
|
||||
|
||||
if (pair.ids[1] == BaseReg::kIdBad)
|
||||
ASMJIT_PROPAGATE(emitter->emit(insts.singleInstId, regs[0], mem));
|
||||
else
|
||||
ASMJIT_PROPAGATE(emitter->emit(insts.pairInstId, regs[0], regs[1], mem));
|
||||
|
||||
mem.resetToFixedOffset();
|
||||
}
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(emitter->ret(x30));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
static Error ASMJIT_CDECL Emitter_emitProlog(BaseEmitter* emitter, const FuncFrame& frame) {
|
||||
EmitHelper emitHelper(emitter);
|
||||
return emitHelper.emitProlog(frame);
|
||||
}
|
||||
|
||||
static Error ASMJIT_CDECL Emitter_emitEpilog(BaseEmitter* emitter, const FuncFrame& frame) {
|
||||
EmitHelper emitHelper(emitter);
|
||||
return emitHelper.emitEpilog(frame);
|
||||
}
|
||||
|
||||
static Error ASMJIT_CDECL Emitter_emitArgsAssignment(BaseEmitter* emitter, const FuncFrame& frame, const FuncArgsAssignment& args) {
|
||||
EmitHelper emitHelper(emitter);
|
||||
return emitHelper.emitArgsAssignment(frame, args);
|
||||
}
|
||||
|
||||
void assignEmitterFuncs(BaseEmitter* emitter) {
|
||||
emitter->_funcs.emitProlog = Emitter_emitProlog;
|
||||
emitter->_funcs.emitEpilog = Emitter_emitEpilog;
|
||||
emitter->_funcs.emitArgsAssignment = Emitter_emitArgsAssignment;
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
emitter->_funcs.formatInstruction = FormatterInternal::formatInstruction;
|
||||
#endif
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
emitter->_funcs.validate = InstInternal::validate;
|
||||
#endif
|
||||
}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_AARCH64
|
||||
50
lib/lepton/asmjit/arm/a64emithelper_p.h
Normal file
50
lib/lepton/asmjit/arm/a64emithelper_p.h
Normal file
@ -0,0 +1,50 @@
|
||||
// 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_ARM_ARMEMITHELPER_P_H_INCLUDED
|
||||
#define ASMJIT_ARM_ARMEMITHELPER_P_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
|
||||
#include "../core/emithelper_p.h"
|
||||
#include "../core/func.h"
|
||||
#include "../arm/a64emitter.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
class EmitHelper : public BaseEmitHelper {
|
||||
public:
|
||||
inline explicit EmitHelper(BaseEmitter* emitter = nullptr) noexcept
|
||||
: BaseEmitHelper(emitter) {}
|
||||
|
||||
Error emitRegMove(
|
||||
const Operand_& dst_,
|
||||
const Operand_& src_, TypeId typeId, const char* comment = nullptr) override;
|
||||
|
||||
Error emitRegSwap(
|
||||
const BaseReg& a,
|
||||
const BaseReg& b, const char* comment = nullptr) override;
|
||||
|
||||
Error emitArgMove(
|
||||
const BaseReg& dst_, TypeId dstTypeId,
|
||||
const Operand_& src_, TypeId srcTypeId, const char* comment = nullptr) override;
|
||||
|
||||
Error emitProlog(const FuncFrame& frame);
|
||||
Error emitEpilog(const FuncFrame& frame);
|
||||
};
|
||||
|
||||
void assignEmitterFuncs(BaseEmitter* emitter);
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_ARMEMITHELPER_P_H_INCLUDED
|
||||
1228
lib/lepton/asmjit/arm/a64emitter.h
Normal file
1228
lib/lepton/asmjit/arm/a64emitter.h
Normal file
File diff suppressed because it is too large
Load Diff
298
lib/lepton/asmjit/arm/a64formatter.cpp
Normal file
298
lib/lepton/asmjit/arm/a64formatter.cpp
Normal file
@ -0,0 +1,298 @@
|
||||
// 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"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/misc_p.h"
|
||||
#include "../core/support.h"
|
||||
#include "../arm/a64formatter_p.h"
|
||||
#include "../arm/a64instapi_p.h"
|
||||
#include "../arm/a64instdb_p.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
#include "../core/compiler.h"
|
||||
#endif
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
// a64::FormatterInternal - Format Register
|
||||
// ========================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(
|
||||
String& sb,
|
||||
FormatFlags flags,
|
||||
const BaseEmitter* emitter,
|
||||
Arch arch,
|
||||
RegType regType,
|
||||
uint32_t rId,
|
||||
uint32_t elementType,
|
||||
uint32_t elementIndex) noexcept {
|
||||
|
||||
DebugUtils::unused(flags);
|
||||
DebugUtils::unused(arch);
|
||||
|
||||
static const char bhsdq[] = "bhsdq";
|
||||
|
||||
bool virtRegFormatted = false;
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
if (Operand::isVirtId(rId)) {
|
||||
if (emitter && emitter->isCompiler()) {
|
||||
const BaseCompiler* cc = static_cast<const BaseCompiler*>(emitter);
|
||||
if (cc->isVirtIdValid(rId)) {
|
||||
VirtReg* vReg = cc->virtRegById(rId);
|
||||
ASMJIT_ASSERT(vReg != nullptr);
|
||||
|
||||
const char* name = vReg->name();
|
||||
if (name && name[0] != '\0')
|
||||
ASMJIT_PROPAGATE(sb.append(name));
|
||||
else
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("%%%u", unsigned(Operand::virtIdToIndex(rId))));
|
||||
|
||||
virtRegFormatted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
DebugUtils::unused(emitter, flags);
|
||||
#endif
|
||||
|
||||
if (!virtRegFormatted) {
|
||||
char letter = '\0';
|
||||
switch (regType) {
|
||||
case RegType::kARM_GpW:
|
||||
if (rId == Gp::kIdZr)
|
||||
return sb.append("wzr");
|
||||
if (rId == Gp::kIdSp)
|
||||
return sb.append("wsp");
|
||||
|
||||
letter = 'w';
|
||||
break;
|
||||
|
||||
case RegType::kARM_GpX:
|
||||
if (rId == Gp::kIdZr)
|
||||
return sb.append("xzr");
|
||||
if (rId == Gp::kIdSp)
|
||||
return sb.append("sp");
|
||||
|
||||
letter = 'x';
|
||||
break;
|
||||
|
||||
case RegType::kARM_VecB:
|
||||
case RegType::kARM_VecH:
|
||||
case RegType::kARM_VecS:
|
||||
case RegType::kARM_VecD:
|
||||
case RegType::kARM_VecV:
|
||||
letter = bhsdq[uint32_t(regType) - uint32_t(RegType::kARM_VecB)];
|
||||
if (elementType)
|
||||
letter = 'v';
|
||||
break;
|
||||
|
||||
default:
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("<Reg-%u>?$u", uint32_t(regType), rId));
|
||||
break;
|
||||
}
|
||||
|
||||
if (letter)
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("%c%u", letter, rId));
|
||||
}
|
||||
|
||||
if (elementType) {
|
||||
char elementLetter = '\0';
|
||||
uint32_t elementCount = 0;
|
||||
|
||||
switch (elementType) {
|
||||
case Vec::kElementTypeB:
|
||||
elementLetter = 'b';
|
||||
elementCount = 16;
|
||||
break;
|
||||
|
||||
case Vec::kElementTypeH:
|
||||
elementLetter = 'h';
|
||||
elementCount = 8;
|
||||
break;
|
||||
|
||||
case Vec::kElementTypeS:
|
||||
elementLetter = 's';
|
||||
elementCount = 4;
|
||||
break;
|
||||
|
||||
case Vec::kElementTypeD:
|
||||
elementLetter = 'd';
|
||||
elementCount = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return sb.append(".<Unknown>");
|
||||
}
|
||||
|
||||
if (elementLetter) {
|
||||
if (elementIndex == 0xFFFFFFFFu) {
|
||||
if (regType == RegType::kARM_VecD)
|
||||
elementCount /= 2u;
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(".%u%c", elementCount, elementLetter));
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(".%c[%u]", elementLetter, elementIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// a64::FormatterInternal - Format Operand
|
||||
// =======================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
|
||||
String& sb,
|
||||
FormatFlags flags,
|
||||
const BaseEmitter* emitter,
|
||||
Arch arch,
|
||||
const Operand_& op) noexcept {
|
||||
|
||||
if (op.isReg()) {
|
||||
const BaseReg& reg = op.as<BaseReg>();
|
||||
|
||||
uint32_t elementType = op.as<Vec>().elementType();
|
||||
uint32_t elementIndex = op.as<Vec>().elementIndex();
|
||||
|
||||
if (!op.as<Vec>().hasElementIndex())
|
||||
elementIndex = 0xFFFFFFFFu;
|
||||
|
||||
return formatRegister(sb, flags, emitter, arch, reg.type(), reg.id(), elementType, elementIndex);
|
||||
}
|
||||
|
||||
if (op.isMem()) {
|
||||
const Mem& m = op.as<Mem>();
|
||||
ASMJIT_PROPAGATE(sb.append('['));
|
||||
|
||||
if (m.hasBase()) {
|
||||
if (m.hasBaseLabel()) {
|
||||
ASMJIT_PROPAGATE(Formatter::formatLabel(sb, flags, emitter, m.baseId()));
|
||||
}
|
||||
else {
|
||||
FormatFlags modifiedFlags = flags;
|
||||
if (m.isRegHome()) {
|
||||
ASMJIT_PROPAGATE(sb.append('&'));
|
||||
modifiedFlags &= ~FormatFlags::kRegCasts;
|
||||
}
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, modifiedFlags, emitter, arch, m.baseType(), m.baseId()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// ARM really requires base.
|
||||
if (m.hasIndex() || m.hasOffset()) {
|
||||
ASMJIT_PROPAGATE(sb.append("<None>"));
|
||||
}
|
||||
}
|
||||
|
||||
// The post index makes it look like there was another operand, but it's
|
||||
// still the part of AsmJit's `arm::Mem` operand so it's consistent with
|
||||
// other architectures.
|
||||
if (m.isPostIndex())
|
||||
ASMJIT_PROPAGATE(sb.append(']'));
|
||||
|
||||
if (m.hasIndex()) {
|
||||
ASMJIT_PROPAGATE(sb.append(", "));
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, flags, emitter, arch, m.indexType(), m.indexId()));
|
||||
}
|
||||
|
||||
if (m.hasOffset()) {
|
||||
ASMJIT_PROPAGATE(sb.append(", "));
|
||||
|
||||
int64_t off = int64_t(m.offset());
|
||||
uint32_t base = 10;
|
||||
|
||||
if (Support::test(flags, FormatFlags::kHexOffsets) && uint64_t(off) > 9)
|
||||
base = 16;
|
||||
|
||||
if (base == 10) {
|
||||
ASMJIT_PROPAGATE(sb.appendInt(off, base));
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(sb.append("0x"));
|
||||
ASMJIT_PROPAGATE(sb.appendUInt(uint64_t(off), base));
|
||||
}
|
||||
}
|
||||
|
||||
if (m.hasShift()) {
|
||||
ASMJIT_PROPAGATE(sb.append(' '));
|
||||
if (!m.isPreOrPost())
|
||||
ASMJIT_PROPAGATE(formatShiftOp(sb, (ShiftOp)m.predicate()));
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(" %u", m.shift()));
|
||||
}
|
||||
|
||||
if (!m.isPostIndex())
|
||||
ASMJIT_PROPAGATE(sb.append(']'));
|
||||
|
||||
if (m.isPreIndex())
|
||||
ASMJIT_PROPAGATE(sb.append('!'));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (op.isImm()) {
|
||||
const Imm& i = op.as<Imm>();
|
||||
int64_t val = i.value();
|
||||
|
||||
if (Support::test(flags, FormatFlags::kHexImms) && uint64_t(val) > 9) {
|
||||
ASMJIT_PROPAGATE(sb.append("0x"));
|
||||
return sb.appendUInt(uint64_t(val), 16);
|
||||
}
|
||||
else {
|
||||
return sb.appendInt(val, 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (op.isLabel()) {
|
||||
return Formatter::formatLabel(sb, flags, emitter, op.id());
|
||||
}
|
||||
|
||||
return sb.append("<None>");
|
||||
}
|
||||
|
||||
// a64::FormatterInternal - Format Instruction
|
||||
// ===========================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatInstruction(
|
||||
String& sb,
|
||||
FormatFlags flags,
|
||||
const BaseEmitter* emitter,
|
||||
Arch arch,
|
||||
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept {
|
||||
|
||||
DebugUtils::unused(arch);
|
||||
|
||||
// Format instruction options and instruction mnemonic.
|
||||
InstId instId = inst.realId();
|
||||
if (instId < Inst::_kIdCount)
|
||||
ASMJIT_PROPAGATE(InstInternal::instIdToString(arch, instId, sb));
|
||||
else
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("[InstId=#%u]", unsigned(instId)));
|
||||
|
||||
CondCode cc = inst.armCondCode();
|
||||
if (cc != CondCode::kAL) {
|
||||
ASMJIT_PROPAGATE(sb.append('.'));
|
||||
ASMJIT_PROPAGATE(formatCondCode(sb, cc));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < opCount; i++) {
|
||||
const Operand_& op = operands[i];
|
||||
if (op.isNone())
|
||||
break;
|
||||
|
||||
ASMJIT_PROPAGATE(sb.append(i == 0 ? " " : ", "));
|
||||
ASMJIT_PROPAGATE(formatOperand(sb, flags, emitter, arch, op));
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_LOGGING
|
||||
59
lib/lepton/asmjit/arm/a64formatter_p.h
Normal file
59
lib/lepton/asmjit/arm/a64formatter_p.h
Normal file
@ -0,0 +1,59 @@
|
||||
// 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_ARM_A64FORMATTER_P_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64FORMATTER_P_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/string.h"
|
||||
#include "../arm/armformatter_p.h"
|
||||
#include "../arm/a64globals.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
namespace FormatterInternal {
|
||||
|
||||
using namespace arm::FormatterInternal;
|
||||
|
||||
Error ASMJIT_CDECL formatRegister(
|
||||
String& sb,
|
||||
FormatFlags flags,
|
||||
const BaseEmitter* emitter,
|
||||
Arch arch,
|
||||
RegType regType,
|
||||
uint32_t regId,
|
||||
uint32_t elementType = 0,
|
||||
uint32_t elementIndex = 0xFFFFFFFFu) noexcept;
|
||||
|
||||
Error ASMJIT_CDECL formatOperand(
|
||||
String& sb,
|
||||
FormatFlags flags,
|
||||
const BaseEmitter* emitter,
|
||||
Arch arch,
|
||||
const Operand_& op) noexcept;
|
||||
|
||||
Error ASMJIT_CDECL formatInstruction(
|
||||
String& sb,
|
||||
FormatFlags flags,
|
||||
const BaseEmitter* emitter,
|
||||
Arch arch,
|
||||
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept;
|
||||
|
||||
} // {FormatterInternal}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_LOGGING
|
||||
#endif // ASMJIT_ARM_A64FORMATTER_P_H_INCLUDED
|
||||
189
lib/lepton/asmjit/arm/a64func.cpp
Normal file
189
lib/lepton/asmjit/arm/a64func.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
// 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"
|
||||
#if !defined(ASMJIT_NO_AARCH64)
|
||||
|
||||
#include "../arm/a64func_p.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
namespace FuncInternal {
|
||||
|
||||
static inline bool shouldThreatAsCDecl(CallConvId ccId) noexcept {
|
||||
return ccId == CallConvId::kCDecl ||
|
||||
ccId == CallConvId::kStdCall ||
|
||||
ccId == CallConvId::kFastCall ||
|
||||
ccId == CallConvId::kVectorCall ||
|
||||
ccId == CallConvId::kThisCall ||
|
||||
ccId == CallConvId::kRegParm1 ||
|
||||
ccId == CallConvId::kRegParm2 ||
|
||||
ccId == CallConvId::kRegParm3;
|
||||
}
|
||||
|
||||
static RegType regTypeFromFpOrVecTypeId(TypeId typeId) noexcept {
|
||||
if (typeId == TypeId::kFloat32)
|
||||
return RegType::kARM_VecS;
|
||||
else if (typeId == TypeId::kFloat64)
|
||||
return RegType::kARM_VecD;
|
||||
else if (TypeUtils::isVec32(typeId))
|
||||
return RegType::kARM_VecS;
|
||||
else if (TypeUtils::isVec64(typeId))
|
||||
return RegType::kARM_VecD;
|
||||
else if (TypeUtils::isVec128(typeId))
|
||||
return RegType::kARM_VecV;
|
||||
else
|
||||
return RegType::kNone;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept {
|
||||
cc.setArch(environment.arch());
|
||||
|
||||
cc.setSaveRestoreRegSize(RegGroup::kGp, 8);
|
||||
cc.setSaveRestoreRegSize(RegGroup::kVec, 8);
|
||||
cc.setSaveRestoreAlignment(RegGroup::kGp, 16);
|
||||
cc.setSaveRestoreAlignment(RegGroup::kVec, 16);
|
||||
cc.setSaveRestoreAlignment(RegGroup::kExtraVirt2, 1);
|
||||
cc.setSaveRestoreAlignment(RegGroup::kExtraVirt3, 1);
|
||||
cc.setPassedOrder(RegGroup::kGp, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(RegGroup::kVec, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
|
||||
if (shouldThreatAsCDecl(ccId)) {
|
||||
// ARM doesn't have that many calling conventions as we can find in X86 world, treat most conventions as __cdecl.
|
||||
cc.setId(CallConvId::kCDecl);
|
||||
cc.setPreservedRegs(RegGroup::kGp, Support::bitMask(Gp::kIdOs, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30));
|
||||
cc.setPreservedRegs(RegGroup::kVec, Support::bitMask(8, 9, 10, 11, 12, 13, 14, 15));
|
||||
}
|
||||
else {
|
||||
cc.setId(ccId);
|
||||
cc.setSaveRestoreRegSize(RegGroup::kVec, 16);
|
||||
cc.setPreservedRegs(RegGroup::kGp, Support::bitMask(4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30));
|
||||
cc.setPreservedRegs(RegGroup::kVec, Support::bitMask(4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31));
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept {
|
||||
DebugUtils::unused(signature);
|
||||
|
||||
const CallConv& cc = func.callConv();
|
||||
uint32_t stackOffset = 0;
|
||||
|
||||
uint32_t i;
|
||||
uint32_t argCount = func.argCount();
|
||||
|
||||
if (func.hasRet()) {
|
||||
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
|
||||
TypeId typeId = func._rets[valueIndex].typeId();
|
||||
|
||||
// Terminate at the first void type (end of the pack).
|
||||
if (typeId == TypeId::kVoid)
|
||||
break;
|
||||
|
||||
switch (typeId) {
|
||||
case TypeId::kInt8:
|
||||
case TypeId::kInt16:
|
||||
case TypeId::kInt32: {
|
||||
func._rets[valueIndex].initReg(RegType::kARM_GpW, valueIndex, TypeId::kInt32);
|
||||
break;
|
||||
}
|
||||
|
||||
case TypeId::kUInt8:
|
||||
case TypeId::kUInt16:
|
||||
case TypeId::kUInt32: {
|
||||
func._rets[valueIndex].initReg(RegType::kARM_GpW, valueIndex, TypeId::kUInt32);
|
||||
break;
|
||||
}
|
||||
|
||||
case TypeId::kInt64:
|
||||
case TypeId::kUInt64: {
|
||||
func._rets[valueIndex].initReg(RegType::kARM_GpX, valueIndex, typeId);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
RegType regType = regTypeFromFpOrVecTypeId(typeId);
|
||||
if (regType == RegType::kNone)
|
||||
return DebugUtils::errored(kErrorInvalidRegType);
|
||||
|
||||
func._rets[valueIndex].initReg(regType, valueIndex, typeId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (cc.strategy()) {
|
||||
case CallConvStrategy::kDefault: {
|
||||
uint32_t gpzPos = 0;
|
||||
uint32_t vecPos = 0;
|
||||
|
||||
for (i = 0; i < argCount; i++) {
|
||||
FuncValue& arg = func._args[i][0];
|
||||
TypeId typeId = arg.typeId();
|
||||
|
||||
if (TypeUtils::isInt(typeId)) {
|
||||
uint32_t regId = BaseReg::kIdBad;
|
||||
|
||||
if (gpzPos < CallConv::kMaxRegArgsPerGroup)
|
||||
regId = cc._passedOrder[RegGroup::kGp].id[gpzPos];
|
||||
|
||||
if (regId != BaseReg::kIdBad) {
|
||||
RegType regType = typeId <= TypeId::kUInt32 ? RegType::kARM_GpW : RegType::kARM_GpX;
|
||||
arg.assignRegData(regType, regId);
|
||||
func.addUsedRegs(RegGroup::kGp, Support::bitMask(regId));
|
||||
gpzPos++;
|
||||
}
|
||||
else {
|
||||
uint32_t size = Support::max<uint32_t>(TypeUtils::sizeOf(typeId), registerSize);
|
||||
arg.assignStackOffset(int32_t(stackOffset));
|
||||
stackOffset += size;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TypeUtils::isFloat(typeId) || TypeUtils::isVec(typeId)) {
|
||||
uint32_t regId = BaseReg::kIdBad;
|
||||
|
||||
if (vecPos < CallConv::kMaxRegArgsPerGroup)
|
||||
regId = cc._passedOrder[RegGroup::kVec].id[vecPos];
|
||||
|
||||
if (regId != BaseReg::kIdBad) {
|
||||
RegType regType = regTypeFromFpOrVecTypeId(typeId);
|
||||
if (regType == RegType::kNone)
|
||||
return DebugUtils::errored(kErrorInvalidRegType);
|
||||
|
||||
arg.initTypeId(typeId);
|
||||
arg.assignRegData(regType, regId);
|
||||
func.addUsedRegs(RegGroup::kVec, Support::bitMask(regId));
|
||||
vecPos++;
|
||||
}
|
||||
else {
|
||||
uint32_t size = TypeUtils::sizeOf(typeId);
|
||||
arg.assignStackOffset(int32_t(stackOffset));
|
||||
stackOffset += size;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
func._argStackSize = stackOffset;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
} // {FuncInternal}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_AARCH64
|
||||
33
lib/lepton/asmjit/arm/a64func_p.h
Normal file
33
lib/lepton/asmjit/arm/a64func_p.h
Normal file
@ -0,0 +1,33 @@
|
||||
// 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_ARM_A64FUNC_P_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64FUNC_P_H_INCLUDED
|
||||
|
||||
#include "../core/func.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
//! AArch64-specific function API (calling conventions and other utilities).
|
||||
namespace FuncInternal {
|
||||
|
||||
//! Initialize `CallConv` structure (AArch64 specific).
|
||||
Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept;
|
||||
|
||||
//! Initialize `FuncDetail` (AArch64 specific).
|
||||
Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept;
|
||||
|
||||
} // {FuncInternal}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_A64FUNC_P_H_INCLUDED
|
||||
1894
lib/lepton/asmjit/arm/a64globals.h
Normal file
1894
lib/lepton/asmjit/arm/a64globals.h
Normal file
File diff suppressed because it is too large
Load Diff
278
lib/lepton/asmjit/arm/a64instapi.cpp
Normal file
278
lib/lepton/asmjit/arm/a64instapi.cpp
Normal file
@ -0,0 +1,278 @@
|
||||
// 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"
|
||||
#if !defined(ASMJIT_NO_AARCH64)
|
||||
|
||||
#include "../core/cpuinfo.h"
|
||||
#include "../core/misc_p.h"
|
||||
#include "../core/support.h"
|
||||
#include "../arm/a64instapi_p.h"
|
||||
#include "../arm/a64instdb_p.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
// a64::InstInternal - Text
|
||||
// ========================
|
||||
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
Error InstInternal::instIdToString(Arch arch, InstId instId, String& output) noexcept {
|
||||
uint32_t realId = instId & uint32_t(InstIdParts::kRealId);
|
||||
DebugUtils::unused(arch);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Inst::isDefinedId(realId)))
|
||||
return DebugUtils::errored(kErrorInvalidInstruction);
|
||||
|
||||
const InstDB::InstInfo& info = InstDB::infoById(realId);
|
||||
return output.append(InstDB::_nameData + info._nameDataIndex);
|
||||
}
|
||||
|
||||
InstId InstInternal::stringToInstId(Arch arch, const char* s, size_t len) noexcept {
|
||||
DebugUtils::unused(arch);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!s))
|
||||
return Inst::kIdNone;
|
||||
|
||||
if (len == SIZE_MAX)
|
||||
len = strlen(s);
|
||||
|
||||
if (ASMJIT_UNLIKELY(len == 0 || len > InstDB::kMaxNameSize))
|
||||
return Inst::kIdNone;
|
||||
|
||||
uint32_t prefix = uint32_t(s[0]) - 'a';
|
||||
if (ASMJIT_UNLIKELY(prefix > 'z' - 'a'))
|
||||
return Inst::kIdNone;
|
||||
|
||||
uint32_t index = InstDB::instNameIndex[prefix].start;
|
||||
if (ASMJIT_UNLIKELY(!index))
|
||||
return Inst::kIdNone;
|
||||
|
||||
const char* nameData = InstDB::_nameData;
|
||||
const InstDB::InstInfo* table = InstDB::_instInfoTable;
|
||||
|
||||
const InstDB::InstInfo* base = table + index;
|
||||
const InstDB::InstInfo* end = table + InstDB::instNameIndex[prefix].end;
|
||||
|
||||
for (size_t lim = (size_t)(end - base); lim != 0; lim >>= 1) {
|
||||
const InstDB::InstInfo* cur = base + (lim >> 1);
|
||||
int result = Support::cmpInstName(nameData + cur[0]._nameDataIndex, s, len);
|
||||
|
||||
if (result < 0) {
|
||||
base = cur + 1;
|
||||
lim--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result > 0)
|
||||
continue;
|
||||
|
||||
return uint32_t((size_t)(cur - table));
|
||||
}
|
||||
|
||||
return Inst::kIdNone;
|
||||
}
|
||||
#endif // !ASMJIT_NO_TEXT
|
||||
|
||||
// a64::InstInternal - Validate
|
||||
// ============================
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
ASMJIT_FAVOR_SIZE Error InstInternal::validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept {
|
||||
// TODO:
|
||||
DebugUtils::unused(arch, inst, operands, opCount, validationFlags);
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif // !ASMJIT_NO_VALIDATION
|
||||
|
||||
// a64::InstInternal - QueryRWInfo
|
||||
// ===============================
|
||||
|
||||
#ifndef ASMJIT_NO_INTROSPECTION
|
||||
struct InstRWInfoData {
|
||||
uint8_t rwx[Globals::kMaxOpCount];
|
||||
};
|
||||
|
||||
static const InstRWInfoData instRWInfoData[] = {
|
||||
#define R uint8_t(OpRWFlags::kRead)
|
||||
#define W uint8_t(OpRWFlags::kWrite)
|
||||
#define X uint8_t(OpRWFlags::kRW)
|
||||
|
||||
{{ R, R, R, R, R, R }}, // kRWI_R
|
||||
{{ R, W, R, R, R, R }}, // kRWI_RW
|
||||
{{ R, X, R, R, R, R }}, // kRWI_RX
|
||||
{{ R, R, W, R, R, R }}, // kRWI_RRW
|
||||
{{ R, W, X, R, R, R }}, // kRWI_RWX
|
||||
{{ W, R, R, R, R, R }}, // kRWI_W
|
||||
{{ W, R, W, R, R, R }}, // kRWI_WRW
|
||||
{{ W, R, X, R, R, R }}, // kRWI_WRX
|
||||
{{ W, R, R, W, R, R }}, // kRWI_WRRW
|
||||
{{ W, R, R, X, R, R }}, // kRWI_WRRX
|
||||
{{ W, W, R, R, R, R }}, // kRWI_WW
|
||||
{{ X, R, R, R, R, R }}, // kRWI_X
|
||||
{{ X, R, X, R, R, R }}, // kRWI_XRX
|
||||
{{ X, X, R, R, X, R }}, // kRWI_XXRRX
|
||||
|
||||
{{ W, R, R, R, R, R }}, // kRWI_LDn
|
||||
{{ R, W, R, R, R, R }}, // kRWI_STn
|
||||
{{ R, R, R, R, R, R }} // kRWI_TODO
|
||||
|
||||
#undef R
|
||||
#undef W
|
||||
#undef X
|
||||
};
|
||||
|
||||
static const uint8_t elementTypeSize[8] = { 0, 1, 2, 4, 8, 4, 4, 0 };
|
||||
|
||||
Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept {
|
||||
// Unused in Release configuration as the assert is not compiled in.
|
||||
DebugUtils::unused(arch);
|
||||
|
||||
// Only called when `arch` matches X86 family.
|
||||
ASMJIT_ASSERT(Environment::isFamilyARM(arch));
|
||||
|
||||
// Get the instruction data.
|
||||
uint32_t realId = inst.id() & uint32_t(InstIdParts::kRealId);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Inst::isDefinedId(realId)))
|
||||
return DebugUtils::errored(kErrorInvalidInstruction);
|
||||
|
||||
out->_instFlags = InstRWFlags::kNone;
|
||||
out->_opCount = uint8_t(opCount);
|
||||
out->_rmFeature = 0;
|
||||
out->_extraReg.reset();
|
||||
out->_readFlags = CpuRWFlags::kNone; // TODO: [ARM] Read PSTATUS.
|
||||
out->_writeFlags = CpuRWFlags::kNone; // TODO: [ARM] Write PSTATUS
|
||||
|
||||
const InstDB::InstInfo& instInfo = InstDB::_instInfoTable[realId];
|
||||
const InstRWInfoData& rwInfo = instRWInfoData[instInfo.rwInfoIndex()];
|
||||
|
||||
if (instInfo.hasFlag(InstDB::kInstFlagConsecutive) && opCount > 2) {
|
||||
for (uint32_t i = 0; i < opCount; i++) {
|
||||
OpRWInfo& op = out->_operands[i];
|
||||
const Operand_& srcOp = operands[i];
|
||||
|
||||
if (!srcOp.isRegOrMem()) {
|
||||
op.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
OpRWFlags rwFlags = i < opCount - 1 ? (OpRWFlags)rwInfo.rwx[0] : (OpRWFlags)rwInfo.rwx[1];
|
||||
|
||||
op._opFlags = rwFlags & ~(OpRWFlags::kZExt);
|
||||
op._physId = BaseReg::kIdBad;
|
||||
op._rmSize = 0;
|
||||
op._resetReserved();
|
||||
|
||||
uint64_t rByteMask = op.isRead() ? 0xFFFFFFFFFFFFFFFFu : 0x0000000000000000u;
|
||||
uint64_t wByteMask = op.isWrite() ? 0xFFFFFFFFFFFFFFFFu : 0x0000000000000000u;
|
||||
|
||||
op._readByteMask = rByteMask;
|
||||
op._writeByteMask = wByteMask;
|
||||
op._extendByteMask = 0;
|
||||
op._consecutiveLeadCount = 0;
|
||||
|
||||
if (srcOp.isReg()) {
|
||||
if (i == 0)
|
||||
op._consecutiveLeadCount = uint8_t(opCount - 1);
|
||||
else
|
||||
op.addOpFlags(OpRWFlags::kConsecutive);
|
||||
}
|
||||
else {
|
||||
const Mem& memOp = srcOp.as<Mem>();
|
||||
|
||||
if (memOp.hasBase()) {
|
||||
op.addOpFlags(OpRWFlags::kMemBaseRead);
|
||||
}
|
||||
|
||||
if (memOp.hasIndex()) {
|
||||
op.addOpFlags(OpRWFlags::kMemIndexRead);
|
||||
op.addOpFlags(memOp.isPreOrPost() ? OpRWFlags::kMemIndexWrite : OpRWFlags::kNone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (uint32_t i = 0; i < opCount; i++) {
|
||||
OpRWInfo& op = out->_operands[i];
|
||||
const Operand_& srcOp = operands[i];
|
||||
|
||||
if (!srcOp.isRegOrMem()) {
|
||||
op.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
OpRWFlags rwFlags = (OpRWFlags)rwInfo.rwx[i];
|
||||
|
||||
op._opFlags = rwFlags & ~(OpRWFlags::kZExt);
|
||||
op._physId = BaseReg::kIdBad;
|
||||
op._rmSize = 0;
|
||||
op._resetReserved();
|
||||
|
||||
uint64_t rByteMask = op.isRead() ? 0xFFFFFFFFFFFFFFFFu : 0x0000000000000000u;
|
||||
uint64_t wByteMask = op.isWrite() ? 0xFFFFFFFFFFFFFFFFu : 0x0000000000000000u;
|
||||
|
||||
op._readByteMask = rByteMask;
|
||||
op._writeByteMask = wByteMask;
|
||||
op._extendByteMask = 0;
|
||||
op._consecutiveLeadCount = 0;
|
||||
|
||||
if (srcOp.isReg()) {
|
||||
if (srcOp.as<Vec>().hasElementIndex()) {
|
||||
// Only part of the vector is accessed if element index [] is used.
|
||||
uint32_t elementType = srcOp.as<Vec>().elementType();
|
||||
uint32_t elementIndex = srcOp.as<Vec>().elementIndex();
|
||||
|
||||
uint32_t elementSize = elementTypeSize[elementType];
|
||||
uint64_t accessMask = uint64_t(Support::lsbMask<uint32_t>(elementSize)) << (elementIndex * elementSize);
|
||||
|
||||
op._readByteMask &= accessMask;
|
||||
op._writeByteMask &= accessMask;
|
||||
}
|
||||
|
||||
// TODO: [ARM] RW info is not finished.
|
||||
}
|
||||
else {
|
||||
const Mem& memOp = srcOp.as<Mem>();
|
||||
|
||||
if (memOp.hasBase()) {
|
||||
op.addOpFlags(OpRWFlags::kMemBaseRead);
|
||||
}
|
||||
|
||||
if (memOp.hasIndex()) {
|
||||
op.addOpFlags(OpRWFlags::kMemIndexRead);
|
||||
op.addOpFlags(memOp.isPreOrPost() ? OpRWFlags::kMemIndexWrite : OpRWFlags::kNone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif // !ASMJIT_NO_INTROSPECTION
|
||||
|
||||
// a64::InstInternal - QueryFeatures
|
||||
// =================================
|
||||
|
||||
#ifndef ASMJIT_NO_INTROSPECTION
|
||||
Error InstInternal::queryFeatures(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept {
|
||||
// TODO: [ARM] QueryFeatures not implemented yet.
|
||||
DebugUtils::unused(arch, inst, operands, opCount, out);
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif // !ASMJIT_NO_INTROSPECTION
|
||||
|
||||
// a64::InstInternal - Unit
|
||||
// ========================
|
||||
|
||||
#if defined(ASMJIT_TEST)
|
||||
UNIT(arm_inst_api_text) {
|
||||
// TODO:
|
||||
}
|
||||
#endif
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_AARCH64
|
||||
41
lib/lepton/asmjit/arm/a64instapi_p.h
Normal file
41
lib/lepton/asmjit/arm/a64instapi_p.h
Normal file
@ -0,0 +1,41 @@
|
||||
// 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_ARM_A64INSTAPI_P_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64INSTAPI_P_H_INCLUDED
|
||||
|
||||
#include "../core/inst.h"
|
||||
#include "../core/operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
namespace InstInternal {
|
||||
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
Error ASMJIT_CDECL instIdToString(Arch arch, InstId instId, String& output) noexcept;
|
||||
InstId ASMJIT_CDECL stringToInstId(Arch arch, const char* s, size_t len) noexcept;
|
||||
#endif // !ASMJIT_NO_TEXT
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
Error ASMJIT_CDECL validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept;
|
||||
#endif // !ASMJIT_NO_VALIDATION
|
||||
|
||||
#ifndef ASMJIT_NO_INTROSPECTION
|
||||
Error ASMJIT_CDECL queryRWInfo(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept;
|
||||
Error ASMJIT_CDECL queryFeatures(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept;
|
||||
#endif // !ASMJIT_NO_INTROSPECTION
|
||||
|
||||
} // {InstInternal}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_A64INSTAPI_P_H_INCLUDED
|
||||
1957
lib/lepton/asmjit/arm/a64instdb.cpp
Normal file
1957
lib/lepton/asmjit/arm/a64instdb.cpp
Normal file
File diff suppressed because it is too large
Load Diff
74
lib/lepton/asmjit/arm/a64instdb.h
Normal file
74
lib/lepton/asmjit/arm/a64instdb.h
Normal file
@ -0,0 +1,74 @@
|
||||
// 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_ARM_A64INSTDB_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64INSTDB_H_INCLUDED
|
||||
|
||||
#include "../arm/a64globals.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
//! Instruction database (AArch64).
|
||||
namespace InstDB {
|
||||
|
||||
//! Instruction flags.
|
||||
enum InstFlags : uint32_t {
|
||||
//! The instruction provides conditional execution.
|
||||
kInstFlagCond = 0x00000001u,
|
||||
//! SIMD instruction that processes elements in pairs.
|
||||
kInstFlagPair = 0x00000002u,
|
||||
//! SIMD instruction that does widening (Long).
|
||||
kInstFlagLong = 0x00000004u,
|
||||
//! SIMD instruction that does narrowing (Narrow).
|
||||
kInstFlagNarrow = 0x00000008u,
|
||||
//! SIMD element access of half-words can only be used with v0..15.
|
||||
kInstFlagVH0_15 = 0x00000010u,
|
||||
|
||||
//! Instruction may consecutive registers if the number of operands is greater than 2.
|
||||
kInstFlagConsecutive = 0x00000080u
|
||||
};
|
||||
|
||||
//! Instruction information (AArch64).
|
||||
struct InstInfo {
|
||||
//! Instruction encoding type.
|
||||
uint32_t _encoding : 8;
|
||||
//! Index to data specific to each encoding type.
|
||||
uint32_t _encodingDataIndex : 8;
|
||||
uint32_t _reserved : 2;
|
||||
//! Index to \ref _nameData.
|
||||
uint32_t _nameDataIndex : 14;
|
||||
|
||||
uint16_t _rwInfoIndex;
|
||||
uint16_t _flags;
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
inline uint32_t rwInfoIndex() const noexcept { return _rwInfoIndex; }
|
||||
inline uint32_t flags() const noexcept { return _flags; }
|
||||
|
||||
inline bool hasFlag(uint32_t flag) const { return (_flags & flag) != 0; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
ASMJIT_VARAPI const InstInfo _instInfoTable[];
|
||||
|
||||
static inline const InstInfo& infoById(InstId instId) noexcept {
|
||||
instId &= uint32_t(InstIdParts::kRealId);
|
||||
ASMJIT_ASSERT(Inst::isDefinedId(instId));
|
||||
return _instInfoTable[instId];
|
||||
}
|
||||
|
||||
} // {InstDB}
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_A64INSTDB_H_INCLUDED
|
||||
876
lib/lepton/asmjit/arm/a64instdb_p.h
Normal file
876
lib/lepton/asmjit/arm/a64instdb_p.h
Normal file
@ -0,0 +1,876 @@
|
||||
// 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_ARM_A64INSTDB_H_P_INCLUDED
|
||||
#define ASMJIT_ARM_A64INSTDB_H_P_INCLUDED
|
||||
|
||||
#include "../core/codeholder.h"
|
||||
#include "../arm/a64instdb.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
namespace InstDB {
|
||||
|
||||
// a64::InstDB - Constants Used by Instructions
|
||||
// ============================================
|
||||
|
||||
// GP register types supported by base instructions.
|
||||
static constexpr uint32_t kW = 0x1;
|
||||
static constexpr uint32_t kX = 0x2;
|
||||
static constexpr uint32_t kWX = 0x3;
|
||||
|
||||
// GP high register IDs supported by the instruction.
|
||||
static constexpr uint32_t kZR = Gp::kIdZr;
|
||||
static constexpr uint32_t kSP = Gp::kIdSp;
|
||||
|
||||
// a64::InstDB - RWInfo
|
||||
// ====================
|
||||
|
||||
enum RWInfoType : uint32_t {
|
||||
kRWI_R,
|
||||
kRWI_RW,
|
||||
kRWI_RX,
|
||||
kRWI_RRW,
|
||||
kRWI_RWX,
|
||||
kRWI_W,
|
||||
kRWI_WRW,
|
||||
kRWI_WRX,
|
||||
kRWI_WRRW,
|
||||
kRWI_WRRX,
|
||||
kRWI_WW,
|
||||
kRWI_X,
|
||||
kRWI_XRX,
|
||||
kRWI_XXRRX,
|
||||
|
||||
kRWI_LDn,
|
||||
kRWI_STn,
|
||||
|
||||
kRWI_SpecialStart = kRWI_LDn
|
||||
};
|
||||
|
||||
// a64::InstDB - ElementType
|
||||
// =========================
|
||||
|
||||
enum ElementType : uint8_t {
|
||||
kET_None = Vec::kElementTypeNone,
|
||||
kET_B = Vec::kElementTypeB,
|
||||
kET_H = Vec::kElementTypeH,
|
||||
kET_S = Vec::kElementTypeS,
|
||||
kET_D = Vec::kElementTypeD,
|
||||
kET_2H = Vec::kElementTypeH2,
|
||||
kET_4B = Vec::kElementTypeB4
|
||||
};
|
||||
|
||||
// a64::InstDB - GpType
|
||||
// ====================
|
||||
|
||||
enum GpType : uint8_t {
|
||||
kGp_W,
|
||||
kGp_X,
|
||||
kGp_X_SP
|
||||
};
|
||||
|
||||
// a64::InstDB - OPSig
|
||||
// ===================
|
||||
|
||||
enum kOpSignature : uint32_t {
|
||||
kOp_GpW = GpW::kSignature,
|
||||
kOp_GpX = GpX::kSignature,
|
||||
|
||||
kOp_B = VecB::kSignature,
|
||||
kOp_H = VecH::kSignature,
|
||||
kOp_S = VecS::kSignature,
|
||||
kOp_D = VecD::kSignature,
|
||||
kOp_Q = VecV::kSignature,
|
||||
|
||||
kOp_V8B = VecD::kSignature | Vec::kSignatureElementB,
|
||||
kOp_V4H = VecD::kSignature | Vec::kSignatureElementH,
|
||||
kOp_V2S = VecD::kSignature | Vec::kSignatureElementS,
|
||||
|
||||
kOp_V16B = VecV::kSignature | Vec::kSignatureElementB,
|
||||
kOp_V8H = VecV::kSignature | Vec::kSignatureElementH,
|
||||
kOp_V4S = VecV::kSignature | Vec::kSignatureElementS,
|
||||
kOp_V2D = VecV::kSignature | Vec::kSignatureElementD
|
||||
};
|
||||
|
||||
// a64::InstDB - HFConv
|
||||
// ====================
|
||||
|
||||
enum kHFConv : uint32_t {
|
||||
//! FP16 version of the instruction is not available.
|
||||
kHF_N,
|
||||
|
||||
//! Doesn't do any change to the opcode.
|
||||
kHF_0,
|
||||
|
||||
kHF_A,
|
||||
kHF_B,
|
||||
kHF_C,
|
||||
kHF_D,
|
||||
|
||||
kHF_Count
|
||||
};
|
||||
|
||||
// a64::InstDB - VOType
|
||||
// ====================
|
||||
|
||||
//! Vector operand type combinations used by FP&SIMD instructions.
|
||||
enum VOType : uint32_t {
|
||||
kVO_V_B,
|
||||
kVO_V_BH,
|
||||
kVO_V_BH_4S,
|
||||
kVO_V_BHS,
|
||||
kVO_V_BHS_D2,
|
||||
kVO_V_HS,
|
||||
kVO_V_S,
|
||||
|
||||
kVO_V_B8H4,
|
||||
kVO_V_B8H4S2,
|
||||
kVO_V_B8D1,
|
||||
kVO_V_H4S2,
|
||||
|
||||
kVO_V_B16,
|
||||
kVO_V_B16H8,
|
||||
kVO_V_B16H8S4,
|
||||
kVO_V_B16D2,
|
||||
kVO_V_H8S4,
|
||||
kVO_V_S4,
|
||||
kVO_V_D2,
|
||||
|
||||
kVO_SV_BHS,
|
||||
kVO_SV_B8H4S2,
|
||||
kVO_SV_HS,
|
||||
kVO_V_Any,
|
||||
kVO_SV_Any,
|
||||
|
||||
kVO_Count
|
||||
};
|
||||
|
||||
// a64::InstDB - EncodingId
|
||||
// ========================
|
||||
|
||||
// ${EncodingId:Begin}
|
||||
// ------------------- Automatically generated, do not edit -------------------
|
||||
enum EncodingId : uint32_t {
|
||||
kEncodingNone = 0,
|
||||
kEncodingBaseAddSub,
|
||||
kEncodingBaseAdr,
|
||||
kEncodingBaseAtDcIcTlbi,
|
||||
kEncodingBaseAtomicCasp,
|
||||
kEncodingBaseAtomicOp,
|
||||
kEncodingBaseAtomicSt,
|
||||
kEncodingBaseBfc,
|
||||
kEncodingBaseBfi,
|
||||
kEncodingBaseBfm,
|
||||
kEncodingBaseBfx,
|
||||
kEncodingBaseBranchCmp,
|
||||
kEncodingBaseBranchReg,
|
||||
kEncodingBaseBranchRel,
|
||||
kEncodingBaseBranchTst,
|
||||
kEncodingBaseCCmp,
|
||||
kEncodingBaseCInc,
|
||||
kEncodingBaseCSel,
|
||||
kEncodingBaseCSet,
|
||||
kEncodingBaseCmpCmn,
|
||||
kEncodingBaseExtend,
|
||||
kEncodingBaseExtract,
|
||||
kEncodingBaseLdSt,
|
||||
kEncodingBaseLdpStp,
|
||||
kEncodingBaseLdxp,
|
||||
kEncodingBaseLogical,
|
||||
kEncodingBaseMov,
|
||||
kEncodingBaseMovKNZ,
|
||||
kEncodingBaseMrs,
|
||||
kEncodingBaseMsr,
|
||||
kEncodingBaseMvnNeg,
|
||||
kEncodingBaseOp,
|
||||
kEncodingBaseOpImm,
|
||||
kEncodingBaseR,
|
||||
kEncodingBaseRM_NoImm,
|
||||
kEncodingBaseRM_SImm10,
|
||||
kEncodingBaseRM_SImm9,
|
||||
kEncodingBaseRR,
|
||||
kEncodingBaseRRII,
|
||||
kEncodingBaseRRR,
|
||||
kEncodingBaseRRRR,
|
||||
kEncodingBaseRev,
|
||||
kEncodingBaseShift,
|
||||
kEncodingBaseStx,
|
||||
kEncodingBaseStxp,
|
||||
kEncodingBaseSys,
|
||||
kEncodingBaseTst,
|
||||
kEncodingFSimdPair,
|
||||
kEncodingFSimdSV,
|
||||
kEncodingFSimdVV,
|
||||
kEncodingFSimdVVV,
|
||||
kEncodingFSimdVVVV,
|
||||
kEncodingFSimdVVVe,
|
||||
kEncodingISimdPair,
|
||||
kEncodingISimdSV,
|
||||
kEncodingISimdVV,
|
||||
kEncodingISimdVVV,
|
||||
kEncodingISimdVVVI,
|
||||
kEncodingISimdVVVV,
|
||||
kEncodingISimdVVVVx,
|
||||
kEncodingISimdVVVe,
|
||||
kEncodingISimdVVVx,
|
||||
kEncodingISimdVVx,
|
||||
kEncodingISimdWWV,
|
||||
kEncodingSimdBicOrr,
|
||||
kEncodingSimdCmp,
|
||||
kEncodingSimdDot,
|
||||
kEncodingSimdDup,
|
||||
kEncodingSimdFcadd,
|
||||
kEncodingSimdFccmpFccmpe,
|
||||
kEncodingSimdFcm,
|
||||
kEncodingSimdFcmla,
|
||||
kEncodingSimdFcmpFcmpe,
|
||||
kEncodingSimdFcsel,
|
||||
kEncodingSimdFcvt,
|
||||
kEncodingSimdFcvtLN,
|
||||
kEncodingSimdFcvtSV,
|
||||
kEncodingSimdFmlal,
|
||||
kEncodingSimdFmov,
|
||||
kEncodingSimdIns,
|
||||
kEncodingSimdLdNStN,
|
||||
kEncodingSimdLdSt,
|
||||
kEncodingSimdLdpStp,
|
||||
kEncodingSimdLdurStur,
|
||||
kEncodingSimdMov,
|
||||
kEncodingSimdMoviMvni,
|
||||
kEncodingSimdShift,
|
||||
kEncodingSimdShiftES,
|
||||
kEncodingSimdSm3tt,
|
||||
kEncodingSimdSmovUmov,
|
||||
kEncodingSimdSxtlUxtl,
|
||||
kEncodingSimdTblTbx
|
||||
};
|
||||
// ----------------------------------------------------------------------------
|
||||
// ${EncodingId:End}
|
||||
|
||||
// a64::InstDB::EncodingData
|
||||
// =========================
|
||||
|
||||
namespace EncodingData {
|
||||
|
||||
#define M_OPCODE(field, bits) \
|
||||
uint32_t _##field : bits; \
|
||||
inline constexpr uint32_t field() const noexcept { return uint32_t(_##field) << (32 - bits); }
|
||||
|
||||
struct BaseOp {
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
struct BaseOpImm {
|
||||
uint32_t opcode;
|
||||
uint16_t immBits;
|
||||
uint16_t immOffset;
|
||||
};
|
||||
|
||||
struct BaseR {
|
||||
uint32_t opcode;
|
||||
uint32_t rType : 8;
|
||||
uint32_t rHiId : 8;
|
||||
uint32_t rShift : 8;
|
||||
};
|
||||
|
||||
struct BaseRR {
|
||||
uint32_t opcode;
|
||||
uint32_t aType : 2;
|
||||
uint32_t aHiId : 6;
|
||||
uint32_t aShift : 5;
|
||||
uint32_t bType : 2;
|
||||
uint32_t bHiId : 6;
|
||||
uint32_t bShift : 5;
|
||||
uint32_t uniform : 1;
|
||||
};
|
||||
|
||||
struct BaseRRR {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t aType : 2;
|
||||
uint32_t aHiId : 6;
|
||||
uint32_t bType : 2;
|
||||
uint32_t bHiId : 6;
|
||||
uint32_t cType : 2;
|
||||
uint32_t cHiId : 6;
|
||||
uint32_t uniform : 1;
|
||||
};
|
||||
|
||||
struct BaseRRRR {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t aType : 2;
|
||||
uint32_t aHiId : 6;
|
||||
uint32_t bType : 2;
|
||||
uint32_t bHiId : 6;
|
||||
uint32_t cType : 2;
|
||||
uint32_t cHiId : 6;
|
||||
uint32_t dType : 2;
|
||||
uint32_t dHiId : 6;
|
||||
uint32_t uniform : 1;
|
||||
};
|
||||
|
||||
struct BaseRRII {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t aType : 2;
|
||||
uint32_t aHiId : 6;
|
||||
uint32_t bType : 2;
|
||||
uint32_t bHiId : 6;
|
||||
uint32_t aImmSize : 6;
|
||||
uint32_t aImmDiscardLsb : 5;
|
||||
uint32_t aImmOffset : 5;
|
||||
uint32_t bImmSize : 6;
|
||||
uint32_t bImmDiscardLsb : 5;
|
||||
uint32_t bImmOffset : 5;
|
||||
};
|
||||
|
||||
struct BaseAtDcIcTlbi {
|
||||
uint32_t immVerifyMask : 14;
|
||||
uint32_t immVerifyData : 14;
|
||||
uint32_t mandatoryReg : 1;
|
||||
};
|
||||
|
||||
struct BaseAdcSbc {
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
struct BaseAddSub {
|
||||
uint32_t shiftedOp : 10; // sf|.......|Sh|.|Rm| Imm:6 |Rn|Rd|
|
||||
uint32_t extendedOp : 10; // sf|.......|..|.|Rm|Opt|Imm3|Rn|Rd|
|
||||
uint32_t immediateOp: 10; // sf|.......|Sh| Imm:12 |Rn|Rd|
|
||||
};
|
||||
|
||||
struct BaseAdr {
|
||||
M_OPCODE(opcode, 22)
|
||||
OffsetType offsetType : 8;
|
||||
};
|
||||
|
||||
struct BaseBfm {
|
||||
uint32_t opcode; // sf|........|N|ImmR:6|ImmS:6|Rn|Rd|
|
||||
};
|
||||
|
||||
struct BaseCmpCmn {
|
||||
uint32_t shiftedOp : 10; // sf|.......|Sh|.|Rm| Imm:6 |Rn|11111|
|
||||
uint32_t extendedOp : 10; // sf|.......|..|.|Rm|Opt|Imm3|Rn|11111|
|
||||
uint32_t immediateOp: 10; // sf|.......|Sh| Imm:12 |Rn|11111|
|
||||
};
|
||||
|
||||
struct BaseExtend {
|
||||
M_OPCODE(opcode, 22) // sf|........|N|......|......|Rn|Rd|
|
||||
uint32_t rType : 2;
|
||||
uint32_t u : 1;
|
||||
};
|
||||
|
||||
struct BaseLogical {
|
||||
uint32_t shiftedOp : 10; // sf|.......|Sh|.|Rm| Imm:6 |Rn|Rd|
|
||||
uint32_t immediateOp: 10; // sf|........|N|ImmR:6|ImmS:6|Rn|Rd|
|
||||
uint32_t negateImm : 1 ; // True if this is an operation that must negate IMM.
|
||||
};
|
||||
|
||||
struct BaseMvnNeg {
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
struct BaseShift {
|
||||
M_OPCODE(registerOp, 22)
|
||||
M_OPCODE(immediateOp, 22)
|
||||
uint32_t ror : 2;
|
||||
};
|
||||
|
||||
struct BaseTst {
|
||||
uint32_t shiftedOp : 10; // sf|.......|Sh|.|Rm| Imm:6 |Rn|11111|
|
||||
uint32_t immediateOp: 10; // sf|........|N|ImmR:6|ImmS:6|Rn|11111|
|
||||
};
|
||||
|
||||
struct BaseRM_NoImm {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t rHiId : 6;
|
||||
uint32_t xOffset : 5;
|
||||
};
|
||||
|
||||
struct BaseRM_SImm9 {
|
||||
M_OPCODE(offsetOp, 22)
|
||||
M_OPCODE(prePostOp, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t rHiId : 6;
|
||||
uint32_t xOffset : 5;
|
||||
uint32_t immShift : 4;
|
||||
};
|
||||
|
||||
struct BaseRM_SImm10 {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t rHiId : 6;
|
||||
uint32_t xOffset : 5;
|
||||
uint32_t immShift : 4;
|
||||
};
|
||||
|
||||
struct BaseLdSt {
|
||||
uint32_t uOffsetOp : 10;
|
||||
uint32_t prePostOp : 11;
|
||||
uint32_t registerOp : 11;
|
||||
uint32_t literalOp : 8;
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
uint32_t uOffsetShift : 3;
|
||||
uint32_t uAltInstId : 14;
|
||||
};
|
||||
|
||||
struct BaseLdpStp {
|
||||
uint32_t offsetOp : 10;
|
||||
uint32_t prePostOp : 10;
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
uint32_t offsetShift : 3;
|
||||
};
|
||||
|
||||
struct BaseStx {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
};
|
||||
|
||||
struct BaseLdxp {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
};
|
||||
|
||||
struct BaseStxp {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
};
|
||||
|
||||
struct BaseAtomicOp {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
uint32_t zr : 1;
|
||||
};
|
||||
|
||||
struct BaseAtomicSt {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
};
|
||||
|
||||
struct BaseAtomicCasp {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t rType : 2;
|
||||
uint32_t xOffset : 5;
|
||||
};
|
||||
|
||||
typedef BaseOp BaseBranchReg;
|
||||
typedef BaseOp BaseBranchRel;
|
||||
typedef BaseOp BaseBranchCmp;
|
||||
typedef BaseOp BaseBranchTst;
|
||||
typedef BaseOp BaseExtract;
|
||||
typedef BaseOp BaseBfc;
|
||||
typedef BaseOp BaseBfi;
|
||||
typedef BaseOp BaseBfx;
|
||||
typedef BaseOp BaseCCmp;
|
||||
typedef BaseOp BaseCInc;
|
||||
typedef BaseOp BaseCSet;
|
||||
typedef BaseOp BaseCSel;
|
||||
typedef BaseOp BaseMovKNZ;
|
||||
typedef BaseOp BaseMull;
|
||||
|
||||
struct FSimdGeneric {
|
||||
uint32_t _scalarOp : 28;
|
||||
uint32_t _scalarHf : 4;
|
||||
uint32_t _vectorOp : 28;
|
||||
uint32_t _vectorHf : 4;
|
||||
|
||||
constexpr uint32_t scalarOp() const noexcept { return uint32_t(_scalarOp) << 10; }
|
||||
constexpr uint32_t vectorOp() const noexcept { return uint32_t(_vectorOp) << 10; }
|
||||
constexpr uint32_t scalarHf() const noexcept { return uint32_t(_scalarHf); }
|
||||
constexpr uint32_t vectorHf() const noexcept { return uint32_t(_vectorHf); }
|
||||
};
|
||||
|
||||
typedef FSimdGeneric FSimdVV;
|
||||
typedef FSimdGeneric FSimdVVV;
|
||||
typedef FSimdGeneric FSimdVVVV;
|
||||
|
||||
struct FSimdSV {
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
struct FSimdVVVe {
|
||||
uint32_t _scalarOp : 28;
|
||||
uint32_t _scalarHf : 4;
|
||||
uint32_t _vectorOp;
|
||||
uint32_t _elementOp;
|
||||
|
||||
constexpr uint32_t scalarOp() const noexcept { return uint32_t(_scalarOp) << 10; }
|
||||
constexpr uint32_t scalarHf() const noexcept { return uint32_t(_scalarHf); };
|
||||
constexpr uint32_t vectorOp() const noexcept { return uint32_t(_vectorOp) << 10; }
|
||||
constexpr uint32_t vectorHf() const noexcept { return kHF_C; }
|
||||
constexpr uint32_t elementScalarOp() const noexcept { return (uint32_t(_elementOp) << 10) | (0x5u << 28); }
|
||||
constexpr uint32_t elementVectorOp() const noexcept { return (uint32_t(_elementOp) << 10); }
|
||||
};
|
||||
|
||||
struct SimdFcadd {
|
||||
uint32_t _opcode;
|
||||
|
||||
constexpr uint32_t opcode() const noexcept { return _opcode << 10; }
|
||||
};
|
||||
|
||||
struct SimdFcmla {
|
||||
uint32_t _regularOp;
|
||||
uint32_t _elementOp;
|
||||
|
||||
constexpr uint32_t regularOp() const noexcept { return uint32_t(_regularOp) << 10; }
|
||||
constexpr uint32_t elementOp() const noexcept { return (uint32_t(_elementOp) << 10); }
|
||||
};
|
||||
|
||||
struct SimdFccmpFccmpe {
|
||||
uint32_t _opcode;
|
||||
constexpr uint32_t opcode() const noexcept { return _opcode; }
|
||||
};
|
||||
|
||||
struct SimdFcm {
|
||||
uint32_t _registerOp : 28;
|
||||
uint32_t _registerHf : 4;
|
||||
|
||||
uint32_t _zeroOp : 28;
|
||||
|
||||
constexpr bool hasRegisterOp() const noexcept { return _registerOp != 0; }
|
||||
constexpr bool hasZeroOp() const noexcept { return _zeroOp != 0; }
|
||||
|
||||
constexpr uint32_t registerScalarOp() const noexcept { return (uint32_t(_registerOp) << 10) | (0x5u << 28); }
|
||||
constexpr uint32_t registerVectorOp() const noexcept { return uint32_t(_registerOp) << 10; }
|
||||
constexpr uint32_t registerScalarHf() const noexcept { return uint32_t(_registerHf); }
|
||||
constexpr uint32_t registerVectorHf() const noexcept { return uint32_t(_registerHf); }
|
||||
|
||||
constexpr uint32_t zeroScalarOp() const noexcept { return (uint32_t(_zeroOp) << 10) | (0x5u << 28); }
|
||||
constexpr uint32_t zeroVectorOp() const noexcept { return (uint32_t(_zeroOp) << 10); }
|
||||
};
|
||||
|
||||
struct SimdFcmpFcmpe {
|
||||
uint32_t _opcode;
|
||||
constexpr uint32_t opcode() const noexcept { return _opcode; }
|
||||
};
|
||||
|
||||
struct SimdFcvtLN {
|
||||
uint32_t _opcode : 22;
|
||||
uint32_t _isCvtxn : 1;
|
||||
uint32_t _hasScalar : 1;
|
||||
|
||||
constexpr uint32_t scalarOp() const noexcept { return (uint32_t(_opcode) << 10) | (0x5u << 28); }
|
||||
constexpr uint32_t vectorOp() const noexcept { return (uint32_t(_opcode) << 10); }
|
||||
|
||||
constexpr uint32_t isCvtxn() const noexcept { return _isCvtxn; }
|
||||
constexpr uint32_t hasScalar() const noexcept { return _hasScalar; }
|
||||
};
|
||||
|
||||
struct SimdFcvtSV {
|
||||
uint32_t _vectorIntOp;
|
||||
uint32_t _vectorFpOp;
|
||||
uint32_t _generalOp : 31;
|
||||
uint32_t _isFloatToInt : 1;
|
||||
|
||||
constexpr uint32_t scalarIntOp() const noexcept { return (uint32_t(_vectorIntOp) << 10) | (0x5u << 28); }
|
||||
constexpr uint32_t vectorIntOp() const noexcept { return uint32_t(_vectorIntOp) << 10; }
|
||||
constexpr uint32_t scalarFpOp() const noexcept { return (uint32_t(_vectorFpOp) << 10) | (0x5u << 28); }
|
||||
constexpr uint32_t vectorFpOp() const noexcept { return uint32_t(_vectorFpOp) << 10; }
|
||||
constexpr uint32_t generalOp() const noexcept { return (uint32_t(_generalOp) << 10); }
|
||||
|
||||
constexpr uint32_t isFloatToInt() const noexcept { return _isFloatToInt; }
|
||||
constexpr uint32_t isFixedPoint() const noexcept { return _vectorFpOp != 0; }
|
||||
};
|
||||
|
||||
struct SimdFmlal {
|
||||
uint32_t _vectorOp;
|
||||
uint32_t _elementOp;
|
||||
uint8_t _optionalQ;
|
||||
uint8_t tA;
|
||||
uint8_t tB;
|
||||
uint8_t tElement;
|
||||
|
||||
constexpr uint32_t vectorOp() const noexcept { return uint32_t(_vectorOp) << 10; }
|
||||
constexpr uint32_t elementOp() const noexcept { return uint32_t(_elementOp) << 10; }
|
||||
constexpr uint32_t optionalQ() const noexcept { return _optionalQ; }
|
||||
};
|
||||
|
||||
struct FSimdPair {
|
||||
uint32_t _scalarOp;
|
||||
uint32_t _vectorOp;
|
||||
|
||||
constexpr uint32_t scalarOp() const noexcept { return uint32_t(_scalarOp) << 10; }
|
||||
constexpr uint32_t vectorOp() const noexcept { return uint32_t(_vectorOp) << 10; }
|
||||
};
|
||||
|
||||
struct ISimdVV {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct ISimdVVx {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t op0Signature;
|
||||
uint32_t op1Signature;
|
||||
};
|
||||
|
||||
struct ISimdSV {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct ISimdVVV {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct ISimdVVVx {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t op0Signature;
|
||||
uint32_t op1Signature;
|
||||
uint32_t op2Signature;
|
||||
};
|
||||
|
||||
struct ISimdWWV {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct ISimdVVVe {
|
||||
uint32_t regularOp : 26; // 22 bits used.
|
||||
uint32_t regularVecType : 6;
|
||||
uint32_t elementOp : 26; // 22 bits used.
|
||||
uint32_t elementVecType : 6;
|
||||
};
|
||||
|
||||
struct ISimdVVVI {
|
||||
M_OPCODE(opcode, 22)
|
||||
uint32_t vecOpType : 6;
|
||||
uint32_t immSize : 4;
|
||||
uint32_t immShift : 4;
|
||||
uint32_t imm64HasOneBitLess : 1;
|
||||
};
|
||||
|
||||
struct ISimdVVVV {
|
||||
uint32_t opcode : 22;
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct ISimdVVVVx {
|
||||
uint32_t opcode;
|
||||
uint32_t op0Signature;
|
||||
uint32_t op1Signature;
|
||||
uint32_t op2Signature;
|
||||
uint32_t op3Signature;
|
||||
};
|
||||
|
||||
struct SimdBicOrr {
|
||||
uint32_t registerOp; // 22 bits used.
|
||||
uint32_t immediateOp; // 22 bits used.
|
||||
};
|
||||
|
||||
struct SimdCmp {
|
||||
uint32_t regOp;
|
||||
uint32_t zeroOp : 22;
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct SimdDot {
|
||||
uint32_t vectorOp; // 22 bits used.
|
||||
uint32_t elementOp; // 22 bits used.
|
||||
uint8_t tA; // Element-type of the first operand.
|
||||
uint8_t tB; // Element-type of the second and third operands.
|
||||
uint8_t tElement; // Element-type of the element index[] operand.
|
||||
};
|
||||
|
||||
struct SimdMoviMvni {
|
||||
uint32_t opcode : 31;
|
||||
uint32_t inverted : 1;
|
||||
};
|
||||
|
||||
struct SimdLdSt {
|
||||
uint32_t uOffsetOp : 10;
|
||||
uint32_t prePostOp : 11;
|
||||
uint32_t registerOp : 11;
|
||||
uint32_t literalOp : 8;
|
||||
uint32_t uAltInstId : 16;
|
||||
};
|
||||
|
||||
struct SimdLdNStN {
|
||||
uint32_t singleOp;
|
||||
uint32_t multipleOp : 22;
|
||||
uint32_t n : 3;
|
||||
uint32_t replicate : 1;
|
||||
};
|
||||
|
||||
struct SimdLdpStp {
|
||||
uint32_t offsetOp : 10;
|
||||
uint32_t prePostOp : 10;
|
||||
};
|
||||
|
||||
struct SimdLdurStur {
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
struct ISimdPair {
|
||||
uint32_t opcode2; // 22 bits used.
|
||||
uint32_t opcode3 : 26; // 22 bits used.
|
||||
uint32_t opType3 : 6;
|
||||
};
|
||||
|
||||
struct SimdShift {
|
||||
uint32_t registerOp; // 22 bits used.
|
||||
uint32_t immediateOp : 22; // 22 bits used.
|
||||
uint32_t invertedImm : 1;
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct SimdShiftES {
|
||||
uint32_t opcode : 22;
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct SimdSm3tt {
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
struct SimdSmovUmov {
|
||||
uint32_t opcode : 22;
|
||||
uint32_t vecOpType : 6;
|
||||
uint32_t isSigned : 1;
|
||||
};
|
||||
|
||||
struct SimdSxtlUxtl {
|
||||
uint32_t opcode : 22;
|
||||
uint32_t vecOpType : 6;
|
||||
};
|
||||
|
||||
struct SimdTblTbx {
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
#undef M_OPCODE
|
||||
|
||||
// ${EncodingDataForward:Begin}
|
||||
// ------------------- Automatically generated, do not edit -------------------
|
||||
extern const BaseAddSub baseAddSub[4];
|
||||
extern const BaseAdr baseAdr[2];
|
||||
extern const BaseAtDcIcTlbi baseAtDcIcTlbi[4];
|
||||
extern const BaseAtomicCasp baseAtomicCasp[4];
|
||||
extern const BaseAtomicOp baseAtomicOp[123];
|
||||
extern const BaseAtomicSt baseAtomicSt[48];
|
||||
extern const BaseBfc baseBfc[1];
|
||||
extern const BaseBfi baseBfi[3];
|
||||
extern const BaseBfm baseBfm[3];
|
||||
extern const BaseBfx baseBfx[3];
|
||||
extern const BaseBranchCmp baseBranchCmp[2];
|
||||
extern const BaseBranchReg baseBranchReg[3];
|
||||
extern const BaseBranchRel baseBranchRel[2];
|
||||
extern const BaseBranchTst baseBranchTst[2];
|
||||
extern const BaseCCmp baseCCmp[2];
|
||||
extern const BaseCInc baseCInc[3];
|
||||
extern const BaseCSel baseCSel[4];
|
||||
extern const BaseCSet baseCSet[2];
|
||||
extern const BaseCmpCmn baseCmpCmn[2];
|
||||
extern const BaseExtend baseExtend[5];
|
||||
extern const BaseExtract baseExtract[1];
|
||||
extern const BaseLdSt baseLdSt[9];
|
||||
extern const BaseLdpStp baseLdpStp[6];
|
||||
extern const BaseLdxp baseLdxp[2];
|
||||
extern const BaseLogical baseLogical[8];
|
||||
extern const BaseMovKNZ baseMovKNZ[3];
|
||||
extern const BaseMvnNeg baseMvnNeg[3];
|
||||
extern const BaseOp baseOp[23];
|
||||
extern const BaseOpImm baseOpImm[14];
|
||||
extern const BaseR baseR[10];
|
||||
extern const BaseRM_NoImm baseRM_NoImm[21];
|
||||
extern const BaseRM_SImm10 baseRM_SImm10[2];
|
||||
extern const BaseRM_SImm9 baseRM_SImm9[23];
|
||||
extern const BaseRR baseRR[15];
|
||||
extern const BaseRRII baseRRII[2];
|
||||
extern const BaseRRR baseRRR[26];
|
||||
extern const BaseRRRR baseRRRR[6];
|
||||
extern const BaseShift baseShift[8];
|
||||
extern const BaseStx baseStx[3];
|
||||
extern const BaseStxp baseStxp[2];
|
||||
extern const BaseTst baseTst[1];
|
||||
extern const FSimdPair fSimdPair[5];
|
||||
extern const FSimdSV fSimdSV[4];
|
||||
extern const FSimdVV fSimdVV[17];
|
||||
extern const FSimdVVV fSimdVVV[13];
|
||||
extern const FSimdVVVV fSimdVVVV[4];
|
||||
extern const FSimdVVVe fSimdVVVe[4];
|
||||
extern const ISimdPair iSimdPair[1];
|
||||
extern const ISimdSV iSimdSV[7];
|
||||
extern const ISimdVV iSimdVV[29];
|
||||
extern const ISimdVVV iSimdVVV[65];
|
||||
extern const ISimdVVVI iSimdVVVI[2];
|
||||
extern const ISimdVVVV iSimdVVVV[2];
|
||||
extern const ISimdVVVVx iSimdVVVVx[1];
|
||||
extern const ISimdVVVe iSimdVVVe[25];
|
||||
extern const ISimdVVVx iSimdVVVx[17];
|
||||
extern const ISimdVVx iSimdVVx[13];
|
||||
extern const ISimdWWV iSimdWWV[8];
|
||||
extern const SimdBicOrr simdBicOrr[2];
|
||||
extern const SimdCmp simdCmp[7];
|
||||
extern const SimdDot simdDot[5];
|
||||
extern const SimdFcadd simdFcadd[1];
|
||||
extern const SimdFccmpFccmpe simdFccmpFccmpe[2];
|
||||
extern const SimdFcm simdFcm[5];
|
||||
extern const SimdFcmla simdFcmla[1];
|
||||
extern const SimdFcmpFcmpe simdFcmpFcmpe[2];
|
||||
extern const SimdFcvtLN simdFcvtLN[6];
|
||||
extern const SimdFcvtSV simdFcvtSV[12];
|
||||
extern const SimdFmlal simdFmlal[6];
|
||||
extern const SimdLdNStN simdLdNStN[12];
|
||||
extern const SimdLdSt simdLdSt[2];
|
||||
extern const SimdLdpStp simdLdpStp[4];
|
||||
extern const SimdLdurStur simdLdurStur[2];
|
||||
extern const SimdMoviMvni simdMoviMvni[2];
|
||||
extern const SimdShift simdShift[40];
|
||||
extern const SimdShiftES simdShiftES[2];
|
||||
extern const SimdSm3tt simdSm3tt[4];
|
||||
extern const SimdSmovUmov simdSmovUmov[2];
|
||||
extern const SimdSxtlUxtl simdSxtlUxtl[4];
|
||||
extern const SimdTblTbx simdTblTbx[2];
|
||||
// ----------------------------------------------------------------------------
|
||||
// ${EncodingDataForward:End}
|
||||
|
||||
} // {EncodingData}
|
||||
|
||||
// a64::InstDB - InstNameIndex
|
||||
// ===========================
|
||||
|
||||
// ${NameLimits:Begin}
|
||||
// ------------------- Automatically generated, do not edit -------------------
|
||||
enum : uint32_t { kMaxNameSize = 9 };
|
||||
// ----------------------------------------------------------------------------
|
||||
// ${NameLimits:End}
|
||||
|
||||
struct InstNameIndex {
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
};
|
||||
|
||||
// a64::InstDB - Tables
|
||||
// ====================
|
||||
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
extern const char _nameData[];
|
||||
extern const InstNameIndex instNameIndex[26];
|
||||
#endif // !ASMJIT_NO_TEXT
|
||||
|
||||
} // {InstDB}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_A64_ARMINSTDB_H_P_INCLUDED
|
||||
|
||||
85
lib/lepton/asmjit/arm/a64operand.cpp
Normal file
85
lib/lepton/asmjit/arm/a64operand.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
// 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"
|
||||
#if !defined(ASMJIT_NO_AARCH64)
|
||||
|
||||
#include "../core/misc_p.h"
|
||||
#include "../arm/a64operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
// a64::Operand - Tests
|
||||
// ====================
|
||||
|
||||
#if defined(ASMJIT_TEST)
|
||||
UNIT(a64_operand) {
|
||||
INFO("Checking if a64::reg(...) matches built-in IDs");
|
||||
EXPECT(w(5) == w5);
|
||||
EXPECT(x(5) == x5);
|
||||
|
||||
INFO("Checking Gp register properties");
|
||||
EXPECT(Gp().isReg() == true);
|
||||
EXPECT(w0.isReg() == true);
|
||||
EXPECT(x0.isReg() == true);
|
||||
EXPECT(w0.id() == 0);
|
||||
EXPECT(x0.id() == 0);
|
||||
EXPECT(wzr.id() == Gp::kIdZr);
|
||||
EXPECT(xzr.id() == Gp::kIdZr);
|
||||
EXPECT(wsp.id() == Gp::kIdSp);
|
||||
EXPECT(sp.id() == Gp::kIdSp);
|
||||
EXPECT(w0.size() == 4);
|
||||
EXPECT(x0.size() == 8);
|
||||
EXPECT(w0.type() == RegType::kARM_GpW);
|
||||
EXPECT(x0.type() == RegType::kARM_GpX);
|
||||
EXPECT(w0.group() == RegGroup::kGp);
|
||||
EXPECT(x0.group() == RegGroup::kGp);
|
||||
|
||||
INFO("Checking Vec register properties");
|
||||
EXPECT(v0.type() == RegType::kARM_VecV);
|
||||
EXPECT(d0.type() == RegType::kARM_VecD);
|
||||
EXPECT(s0.type() == RegType::kARM_VecS);
|
||||
EXPECT(h0.type() == RegType::kARM_VecH);
|
||||
EXPECT(b0.type() == RegType::kARM_VecB);
|
||||
|
||||
EXPECT(v0.group() == RegGroup::kVec);
|
||||
EXPECT(d0.group() == RegGroup::kVec);
|
||||
EXPECT(s0.group() == RegGroup::kVec);
|
||||
EXPECT(h0.group() == RegGroup::kVec);
|
||||
EXPECT(b0.group() == RegGroup::kVec);
|
||||
|
||||
INFO("Checking Vec register element[] access");
|
||||
Vec vd_1 = v15.d(1);
|
||||
EXPECT(vd_1.type() == RegType::kARM_VecV);
|
||||
EXPECT(vd_1.group() == RegGroup::kVec);
|
||||
EXPECT(vd_1.id() == 15);
|
||||
EXPECT(vd_1.isVecD2());
|
||||
EXPECT(vd_1.elementType() == Vec::kElementTypeD);
|
||||
EXPECT(vd_1.hasElementIndex());
|
||||
EXPECT(vd_1.elementIndex() == 1);
|
||||
|
||||
Vec vs_3 = v15.s(3);
|
||||
EXPECT(vs_3.type() == RegType::kARM_VecV);
|
||||
EXPECT(vs_3.group() == RegGroup::kVec);
|
||||
EXPECT(vs_3.id() == 15);
|
||||
EXPECT(vs_3.isVecS4());
|
||||
EXPECT(vs_3.elementType() == Vec::kElementTypeS);
|
||||
EXPECT(vs_3.hasElementIndex());
|
||||
EXPECT(vs_3.elementIndex() == 3);
|
||||
|
||||
Vec vb_4 = v15.b4(3);
|
||||
EXPECT(vb_4.type() == RegType::kARM_VecV);
|
||||
EXPECT(vb_4.group() == RegGroup::kVec);
|
||||
EXPECT(vb_4.id() == 15);
|
||||
EXPECT(vb_4.isVecB4x4());
|
||||
EXPECT(vb_4.elementType() == Vec::kElementTypeB4);
|
||||
EXPECT(vb_4.hasElementIndex());
|
||||
EXPECT(vb_4.elementIndex() == 3);
|
||||
}
|
||||
#endif
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_AARCH64
|
||||
312
lib/lepton/asmjit/arm/a64operand.h
Normal file
312
lib/lepton/asmjit/arm/a64operand.h
Normal file
@ -0,0 +1,312 @@
|
||||
// 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_ARM_A64OPERAND_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64OPERAND_H_INCLUDED
|
||||
|
||||
#include "../arm/armoperand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
using arm::Reg;
|
||||
using arm::Mem;
|
||||
using arm::Gp;
|
||||
using arm::GpW;
|
||||
using arm::GpX;
|
||||
|
||||
using arm::Vec;
|
||||
using arm::VecB;
|
||||
using arm::VecH;
|
||||
using arm::VecS;
|
||||
using arm::VecD;
|
||||
using arm::VecV;
|
||||
|
||||
#ifndef _DOXYGEN
|
||||
namespace regs {
|
||||
#endif
|
||||
|
||||
using namespace ::asmjit::arm::regs;
|
||||
|
||||
static constexpr GpW w0 = GpW(0);
|
||||
static constexpr GpW w1 = GpW(1);
|
||||
static constexpr GpW w2 = GpW(2);
|
||||
static constexpr GpW w3 = GpW(3);
|
||||
static constexpr GpW w4 = GpW(4);
|
||||
static constexpr GpW w5 = GpW(5);
|
||||
static constexpr GpW w6 = GpW(6);
|
||||
static constexpr GpW w7 = GpW(7);
|
||||
static constexpr GpW w8 = GpW(8);
|
||||
static constexpr GpW w9 = GpW(9);
|
||||
static constexpr GpW w10 = GpW(10);
|
||||
static constexpr GpW w11 = GpW(11);
|
||||
static constexpr GpW w12 = GpW(12);
|
||||
static constexpr GpW w13 = GpW(13);
|
||||
static constexpr GpW w14 = GpW(14);
|
||||
static constexpr GpW w15 = GpW(15);
|
||||
static constexpr GpW w16 = GpW(16);
|
||||
static constexpr GpW w17 = GpW(17);
|
||||
static constexpr GpW w18 = GpW(18);
|
||||
static constexpr GpW w19 = GpW(19);
|
||||
static constexpr GpW w20 = GpW(20);
|
||||
static constexpr GpW w21 = GpW(21);
|
||||
static constexpr GpW w22 = GpW(22);
|
||||
static constexpr GpW w23 = GpW(23);
|
||||
static constexpr GpW w24 = GpW(24);
|
||||
static constexpr GpW w25 = GpW(25);
|
||||
static constexpr GpW w26 = GpW(26);
|
||||
static constexpr GpW w27 = GpW(27);
|
||||
static constexpr GpW w28 = GpW(28);
|
||||
static constexpr GpW w29 = GpW(29);
|
||||
static constexpr GpW w30 = GpW(30);
|
||||
static constexpr GpW wzr = GpW(Gp::kIdZr);
|
||||
static constexpr GpW wsp = GpW(Gp::kIdSp);
|
||||
|
||||
static constexpr GpX x0 = GpX(0);
|
||||
static constexpr GpX x1 = GpX(1);
|
||||
static constexpr GpX x2 = GpX(2);
|
||||
static constexpr GpX x3 = GpX(3);
|
||||
static constexpr GpX x4 = GpX(4);
|
||||
static constexpr GpX x5 = GpX(5);
|
||||
static constexpr GpX x6 = GpX(6);
|
||||
static constexpr GpX x7 = GpX(7);
|
||||
static constexpr GpX x8 = GpX(8);
|
||||
static constexpr GpX x9 = GpX(9);
|
||||
static constexpr GpX x10 = GpX(10);
|
||||
static constexpr GpX x11 = GpX(11);
|
||||
static constexpr GpX x12 = GpX(12);
|
||||
static constexpr GpX x13 = GpX(13);
|
||||
static constexpr GpX x14 = GpX(14);
|
||||
static constexpr GpX x15 = GpX(15);
|
||||
static constexpr GpX x16 = GpX(16);
|
||||
static constexpr GpX x17 = GpX(17);
|
||||
static constexpr GpX x18 = GpX(18);
|
||||
static constexpr GpX x19 = GpX(19);
|
||||
static constexpr GpX x20 = GpX(20);
|
||||
static constexpr GpX x21 = GpX(21);
|
||||
static constexpr GpX x22 = GpX(22);
|
||||
static constexpr GpX x23 = GpX(23);
|
||||
static constexpr GpX x24 = GpX(24);
|
||||
static constexpr GpX x25 = GpX(25);
|
||||
static constexpr GpX x26 = GpX(26);
|
||||
static constexpr GpX x27 = GpX(27);
|
||||
static constexpr GpX x28 = GpX(28);
|
||||
static constexpr GpX x29 = GpX(29);
|
||||
static constexpr GpX x30 = GpX(30);
|
||||
static constexpr GpX xzr = GpX(Gp::kIdZr);
|
||||
static constexpr GpX sp = GpX(Gp::kIdSp);
|
||||
|
||||
static constexpr VecB b0 = VecB(0);
|
||||
static constexpr VecB b1 = VecB(1);
|
||||
static constexpr VecB b2 = VecB(2);
|
||||
static constexpr VecB b3 = VecB(3);
|
||||
static constexpr VecB b4 = VecB(4);
|
||||
static constexpr VecB b5 = VecB(5);
|
||||
static constexpr VecB b6 = VecB(6);
|
||||
static constexpr VecB b7 = VecB(7);
|
||||
static constexpr VecB b8 = VecB(8);
|
||||
static constexpr VecB b9 = VecB(9);
|
||||
static constexpr VecB b10 = VecB(10);
|
||||
static constexpr VecB b11 = VecB(11);
|
||||
static constexpr VecB b12 = VecB(12);
|
||||
static constexpr VecB b13 = VecB(13);
|
||||
static constexpr VecB b14 = VecB(14);
|
||||
static constexpr VecB b15 = VecB(15);
|
||||
static constexpr VecB b16 = VecB(16);
|
||||
static constexpr VecB b17 = VecB(17);
|
||||
static constexpr VecB b18 = VecB(18);
|
||||
static constexpr VecB b19 = VecB(19);
|
||||
static constexpr VecB b20 = VecB(20);
|
||||
static constexpr VecB b21 = VecB(21);
|
||||
static constexpr VecB b22 = VecB(22);
|
||||
static constexpr VecB b23 = VecB(23);
|
||||
static constexpr VecB b24 = VecB(24);
|
||||
static constexpr VecB b25 = VecB(25);
|
||||
static constexpr VecB b26 = VecB(26);
|
||||
static constexpr VecB b27 = VecB(27);
|
||||
static constexpr VecB b28 = VecB(28);
|
||||
static constexpr VecB b29 = VecB(29);
|
||||
static constexpr VecB b30 = VecB(30);
|
||||
static constexpr VecB b31 = VecB(31);
|
||||
|
||||
static constexpr VecH h0 = VecH(0);
|
||||
static constexpr VecH h1 = VecH(1);
|
||||
static constexpr VecH h2 = VecH(2);
|
||||
static constexpr VecH h3 = VecH(3);
|
||||
static constexpr VecH h4 = VecH(4);
|
||||
static constexpr VecH h5 = VecH(5);
|
||||
static constexpr VecH h6 = VecH(6);
|
||||
static constexpr VecH h7 = VecH(7);
|
||||
static constexpr VecH h8 = VecH(8);
|
||||
static constexpr VecH h9 = VecH(9);
|
||||
static constexpr VecH h10 = VecH(10);
|
||||
static constexpr VecH h11 = VecH(11);
|
||||
static constexpr VecH h12 = VecH(12);
|
||||
static constexpr VecH h13 = VecH(13);
|
||||
static constexpr VecH h14 = VecH(14);
|
||||
static constexpr VecH h15 = VecH(15);
|
||||
static constexpr VecH h16 = VecH(16);
|
||||
static constexpr VecH h17 = VecH(17);
|
||||
static constexpr VecH h18 = VecH(18);
|
||||
static constexpr VecH h19 = VecH(19);
|
||||
static constexpr VecH h20 = VecH(20);
|
||||
static constexpr VecH h21 = VecH(21);
|
||||
static constexpr VecH h22 = VecH(22);
|
||||
static constexpr VecH h23 = VecH(23);
|
||||
static constexpr VecH h24 = VecH(24);
|
||||
static constexpr VecH h25 = VecH(25);
|
||||
static constexpr VecH h26 = VecH(26);
|
||||
static constexpr VecH h27 = VecH(27);
|
||||
static constexpr VecH h28 = VecH(28);
|
||||
static constexpr VecH h29 = VecH(29);
|
||||
static constexpr VecH h30 = VecH(30);
|
||||
static constexpr VecH h31 = VecH(31);
|
||||
|
||||
static constexpr VecS s0 = VecS(0);
|
||||
static constexpr VecS s1 = VecS(1);
|
||||
static constexpr VecS s2 = VecS(2);
|
||||
static constexpr VecS s3 = VecS(3);
|
||||
static constexpr VecS s4 = VecS(4);
|
||||
static constexpr VecS s5 = VecS(5);
|
||||
static constexpr VecS s6 = VecS(6);
|
||||
static constexpr VecS s7 = VecS(7);
|
||||
static constexpr VecS s8 = VecS(8);
|
||||
static constexpr VecS s9 = VecS(9);
|
||||
static constexpr VecS s10 = VecS(10);
|
||||
static constexpr VecS s11 = VecS(11);
|
||||
static constexpr VecS s12 = VecS(12);
|
||||
static constexpr VecS s13 = VecS(13);
|
||||
static constexpr VecS s14 = VecS(14);
|
||||
static constexpr VecS s15 = VecS(15);
|
||||
static constexpr VecS s16 = VecS(16);
|
||||
static constexpr VecS s17 = VecS(17);
|
||||
static constexpr VecS s18 = VecS(18);
|
||||
static constexpr VecS s19 = VecS(19);
|
||||
static constexpr VecS s20 = VecS(20);
|
||||
static constexpr VecS s21 = VecS(21);
|
||||
static constexpr VecS s22 = VecS(22);
|
||||
static constexpr VecS s23 = VecS(23);
|
||||
static constexpr VecS s24 = VecS(24);
|
||||
static constexpr VecS s25 = VecS(25);
|
||||
static constexpr VecS s26 = VecS(26);
|
||||
static constexpr VecS s27 = VecS(27);
|
||||
static constexpr VecS s28 = VecS(28);
|
||||
static constexpr VecS s29 = VecS(29);
|
||||
static constexpr VecS s30 = VecS(30);
|
||||
static constexpr VecS s31 = VecS(31);
|
||||
|
||||
static constexpr VecD d0 = VecD(0);
|
||||
static constexpr VecD d1 = VecD(1);
|
||||
static constexpr VecD d2 = VecD(2);
|
||||
static constexpr VecD d3 = VecD(3);
|
||||
static constexpr VecD d4 = VecD(4);
|
||||
static constexpr VecD d5 = VecD(5);
|
||||
static constexpr VecD d6 = VecD(6);
|
||||
static constexpr VecD d7 = VecD(7);
|
||||
static constexpr VecD d8 = VecD(8);
|
||||
static constexpr VecD d9 = VecD(9);
|
||||
static constexpr VecD d10 = VecD(10);
|
||||
static constexpr VecD d11 = VecD(11);
|
||||
static constexpr VecD d12 = VecD(12);
|
||||
static constexpr VecD d13 = VecD(13);
|
||||
static constexpr VecD d14 = VecD(14);
|
||||
static constexpr VecD d15 = VecD(15);
|
||||
static constexpr VecD d16 = VecD(16);
|
||||
static constexpr VecD d17 = VecD(17);
|
||||
static constexpr VecD d18 = VecD(18);
|
||||
static constexpr VecD d19 = VecD(19);
|
||||
static constexpr VecD d20 = VecD(20);
|
||||
static constexpr VecD d21 = VecD(21);
|
||||
static constexpr VecD d22 = VecD(22);
|
||||
static constexpr VecD d23 = VecD(23);
|
||||
static constexpr VecD d24 = VecD(24);
|
||||
static constexpr VecD d25 = VecD(25);
|
||||
static constexpr VecD d26 = VecD(26);
|
||||
static constexpr VecD d27 = VecD(27);
|
||||
static constexpr VecD d28 = VecD(28);
|
||||
static constexpr VecD d29 = VecD(29);
|
||||
static constexpr VecD d30 = VecD(30);
|
||||
static constexpr VecD d31 = VecD(31);
|
||||
|
||||
static constexpr VecV q0 = VecV(0);
|
||||
static constexpr VecV q1 = VecV(1);
|
||||
static constexpr VecV q2 = VecV(2);
|
||||
static constexpr VecV q3 = VecV(3);
|
||||
static constexpr VecV q4 = VecV(4);
|
||||
static constexpr VecV q5 = VecV(5);
|
||||
static constexpr VecV q6 = VecV(6);
|
||||
static constexpr VecV q7 = VecV(7);
|
||||
static constexpr VecV q8 = VecV(8);
|
||||
static constexpr VecV q9 = VecV(9);
|
||||
static constexpr VecV q10 = VecV(10);
|
||||
static constexpr VecV q11 = VecV(11);
|
||||
static constexpr VecV q12 = VecV(12);
|
||||
static constexpr VecV q13 = VecV(13);
|
||||
static constexpr VecV q14 = VecV(14);
|
||||
static constexpr VecV q15 = VecV(15);
|
||||
static constexpr VecV q16 = VecV(16);
|
||||
static constexpr VecV q17 = VecV(17);
|
||||
static constexpr VecV q18 = VecV(18);
|
||||
static constexpr VecV q19 = VecV(19);
|
||||
static constexpr VecV q20 = VecV(20);
|
||||
static constexpr VecV q21 = VecV(21);
|
||||
static constexpr VecV q22 = VecV(22);
|
||||
static constexpr VecV q23 = VecV(23);
|
||||
static constexpr VecV q24 = VecV(24);
|
||||
static constexpr VecV q25 = VecV(25);
|
||||
static constexpr VecV q26 = VecV(26);
|
||||
static constexpr VecV q27 = VecV(27);
|
||||
static constexpr VecV q28 = VecV(28);
|
||||
static constexpr VecV q29 = VecV(29);
|
||||
static constexpr VecV q30 = VecV(30);
|
||||
static constexpr VecV q31 = VecV(31);
|
||||
|
||||
static constexpr VecV v0 = VecV(0);
|
||||
static constexpr VecV v1 = VecV(1);
|
||||
static constexpr VecV v2 = VecV(2);
|
||||
static constexpr VecV v3 = VecV(3);
|
||||
static constexpr VecV v4 = VecV(4);
|
||||
static constexpr VecV v5 = VecV(5);
|
||||
static constexpr VecV v6 = VecV(6);
|
||||
static constexpr VecV v7 = VecV(7);
|
||||
static constexpr VecV v8 = VecV(8);
|
||||
static constexpr VecV v9 = VecV(9);
|
||||
static constexpr VecV v10 = VecV(10);
|
||||
static constexpr VecV v11 = VecV(11);
|
||||
static constexpr VecV v12 = VecV(12);
|
||||
static constexpr VecV v13 = VecV(13);
|
||||
static constexpr VecV v14 = VecV(14);
|
||||
static constexpr VecV v15 = VecV(15);
|
||||
static constexpr VecV v16 = VecV(16);
|
||||
static constexpr VecV v17 = VecV(17);
|
||||
static constexpr VecV v18 = VecV(18);
|
||||
static constexpr VecV v19 = VecV(19);
|
||||
static constexpr VecV v20 = VecV(20);
|
||||
static constexpr VecV v21 = VecV(21);
|
||||
static constexpr VecV v22 = VecV(22);
|
||||
static constexpr VecV v23 = VecV(23);
|
||||
static constexpr VecV v24 = VecV(24);
|
||||
static constexpr VecV v25 = VecV(25);
|
||||
static constexpr VecV v26 = VecV(26);
|
||||
static constexpr VecV v27 = VecV(27);
|
||||
static constexpr VecV v28 = VecV(28);
|
||||
static constexpr VecV v29 = VecV(29);
|
||||
static constexpr VecV v30 = VecV(30);
|
||||
static constexpr VecV v31 = VecV(31);
|
||||
|
||||
#ifndef _DOXYGEN
|
||||
} // {regs}
|
||||
|
||||
// Make `a64::regs` accessible through `a64` namespace as well.
|
||||
using namespace regs;
|
||||
#endif
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_A64OPERAND_H_INCLUDED
|
||||
852
lib/lepton/asmjit/arm/a64rapass.cpp
Normal file
852
lib/lepton/asmjit/arm/a64rapass.cpp
Normal file
@ -0,0 +1,852 @@
|
||||
// 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"
|
||||
#if !defined(ASMJIT_NO_AARCH64) && !defined(ASMJIT_NO_COMPILER)
|
||||
|
||||
#include "../core/cpuinfo.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
#include "../arm/a64assembler.h"
|
||||
#include "../arm/a64compiler.h"
|
||||
#include "../arm/a64emithelper_p.h"
|
||||
#include "../arm/a64instapi_p.h"
|
||||
#include "../arm/a64instdb_p.h"
|
||||
#include "../arm/a64rapass_p.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
// a64::ARMRAPass - Helpers
|
||||
// ========================
|
||||
|
||||
// TODO: [ARM] These should be shared with all backends.
|
||||
ASMJIT_MAYBE_UNUSED
|
||||
static inline uint64_t raImmMaskFromSize(uint32_t size) noexcept {
|
||||
ASMJIT_ASSERT(size > 0 && size < 256);
|
||||
static const uint64_t masks[] = {
|
||||
0x00000000000000FFu, // 1
|
||||
0x000000000000FFFFu, // 2
|
||||
0x00000000FFFFFFFFu, // 4
|
||||
0xFFFFFFFFFFFFFFFFu, // 8
|
||||
0x0000000000000000u, // 16
|
||||
0x0000000000000000u, // 32
|
||||
0x0000000000000000u, // 64
|
||||
0x0000000000000000u, // 128
|
||||
0x0000000000000000u // 256
|
||||
};
|
||||
return masks[Support::ctz(size)];
|
||||
}
|
||||
|
||||
static const RegMask raConsecutiveLeadCountToRegMaskFilter[5] = {
|
||||
0xFFFFFFFFu, // [0] No consecutive.
|
||||
0x00000000u, // [1] Invalid, never used.
|
||||
0x7FFFFFFFu, // [2] 2 consecutive registers.
|
||||
0x3FFFFFFFu, // [3] 3 consecutive registers.
|
||||
0x1FFFFFFFu // [4] 4 consecutive registers.
|
||||
};
|
||||
|
||||
static inline RATiedFlags raUseOutFlagsFromRWFlags(OpRWFlags rwFlags) noexcept {
|
||||
static constexpr RATiedFlags map[] = {
|
||||
RATiedFlags::kNone,
|
||||
RATiedFlags::kRead | RATiedFlags::kUse, // kRead
|
||||
RATiedFlags::kWrite | RATiedFlags::kOut, // kWrite
|
||||
RATiedFlags::kRW | RATiedFlags::kUse, // kRW
|
||||
};
|
||||
|
||||
return map[uint32_t(rwFlags & OpRWFlags::kRW)];
|
||||
}
|
||||
|
||||
static inline RATiedFlags raRegRwFlags(OpRWFlags flags) noexcept {
|
||||
return raUseOutFlagsFromRWFlags(flags);
|
||||
}
|
||||
|
||||
static inline RATiedFlags raMemBaseRwFlags(OpRWFlags flags) noexcept {
|
||||
constexpr uint32_t shift = Support::ConstCTZ<uint32_t(OpRWFlags::kMemBaseRW)>::value;
|
||||
return raUseOutFlagsFromRWFlags(OpRWFlags(uint32_t(flags) >> shift) & OpRWFlags::kRW);
|
||||
}
|
||||
|
||||
static inline RATiedFlags raMemIndexRwFlags(OpRWFlags flags) noexcept {
|
||||
constexpr uint32_t shift = Support::ConstCTZ<uint32_t(OpRWFlags::kMemIndexRW)>::value;
|
||||
return raUseOutFlagsFromRWFlags(OpRWFlags(uint32_t(flags) >> shift) & OpRWFlags::kRW);
|
||||
}
|
||||
// a64::RACFGBuilder
|
||||
// =================
|
||||
|
||||
class RACFGBuilder : public RACFGBuilderT<RACFGBuilder> {
|
||||
public:
|
||||
Arch _arch;
|
||||
|
||||
inline RACFGBuilder(ARMRAPass* pass) noexcept
|
||||
: RACFGBuilderT<RACFGBuilder>(pass),
|
||||
_arch(pass->cc()->arch()) {}
|
||||
|
||||
inline Compiler* cc() const noexcept { return static_cast<Compiler*>(_cc); }
|
||||
|
||||
Error onInst(InstNode* inst, InstControlFlow& controlType, RAInstBuilder& ib) noexcept;
|
||||
|
||||
Error onBeforeInvoke(InvokeNode* invokeNode) noexcept;
|
||||
Error onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept;
|
||||
|
||||
Error moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept;
|
||||
Error moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept;
|
||||
Error moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept;
|
||||
|
||||
Error onBeforeRet(FuncRetNode* funcRet) noexcept;
|
||||
Error onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept;
|
||||
};
|
||||
|
||||
// a64::RACFGBuilder - OnInst
|
||||
// ==========================
|
||||
|
||||
// TODO: [ARM] This is just a workaround...
|
||||
static InstControlFlow getControlFlowType(InstId instId) noexcept {
|
||||
switch (BaseInst::extractRealId(instId)) {
|
||||
case Inst::kIdB:
|
||||
case Inst::kIdBr:
|
||||
if (BaseInst::extractARMCondCode(instId) == CondCode::kAL)
|
||||
return InstControlFlow::kJump;
|
||||
else
|
||||
return InstControlFlow::kBranch;
|
||||
case Inst::kIdBl:
|
||||
case Inst::kIdBlr:
|
||||
return InstControlFlow::kCall;
|
||||
case Inst::kIdCbz:
|
||||
case Inst::kIdCbnz:
|
||||
case Inst::kIdTbz:
|
||||
case Inst::kIdTbnz:
|
||||
return InstControlFlow::kBranch;
|
||||
case Inst::kIdRet:
|
||||
return InstControlFlow::kReturn;
|
||||
default:
|
||||
return InstControlFlow::kRegular;
|
||||
}
|
||||
}
|
||||
|
||||
Error RACFGBuilder::onInst(InstNode* inst, InstControlFlow& controlType, RAInstBuilder& ib) noexcept {
|
||||
InstRWInfo rwInfo;
|
||||
|
||||
if (Inst::isDefinedId(inst->realId())) {
|
||||
InstId instId = inst->id();
|
||||
uint32_t opCount = inst->opCount();
|
||||
const Operand* opArray = inst->operands();
|
||||
ASMJIT_PROPAGATE(InstInternal::queryRWInfo(_arch, inst->baseInst(), opArray, opCount, &rwInfo));
|
||||
|
||||
const InstDB::InstInfo& instInfo = InstDB::infoById(instId);
|
||||
uint32_t singleRegOps = 0;
|
||||
|
||||
ib.addInstRWFlags(rwInfo.instFlags());
|
||||
|
||||
if (opCount) {
|
||||
uint32_t consecutiveOffset = 0xFFFFFFFFu;
|
||||
uint32_t consecutiveParent = Globals::kInvalidId;
|
||||
|
||||
for (uint32_t i = 0; i < opCount; i++) {
|
||||
const Operand& op = opArray[i];
|
||||
const OpRWInfo& opRwInfo = rwInfo.operand(i);
|
||||
|
||||
if (op.isReg()) {
|
||||
// Register Operand
|
||||
// ----------------
|
||||
const Reg& reg = op.as<Reg>();
|
||||
|
||||
RATiedFlags flags = raRegRwFlags(opRwInfo.opFlags());
|
||||
uint32_t vIndex = Operand::virtIdToIndex(reg.id());
|
||||
|
||||
if (vIndex < Operand::kVirtIdCount) {
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
|
||||
|
||||
// Use RW instead of Write in case that not the whole register is overwritten. This is important for
|
||||
// liveness as we cannot kill a register that will be used.
|
||||
if ((flags & RATiedFlags::kRW) == RATiedFlags::kWrite) {
|
||||
if (workReg->regByteMask() & ~(opRwInfo.writeByteMask() | opRwInfo.extendByteMask())) {
|
||||
// Not write-only operation.
|
||||
flags = (flags & ~RATiedFlags::kOut) | (RATiedFlags::kRead | RATiedFlags::kUse);
|
||||
}
|
||||
}
|
||||
|
||||
RegGroup group = workReg->group();
|
||||
|
||||
RegMask useRegs = _pass->_availableRegs[group];
|
||||
RegMask outRegs = useRegs;
|
||||
|
||||
uint32_t useId = BaseReg::kIdBad;
|
||||
uint32_t outId = BaseReg::kIdBad;
|
||||
|
||||
uint32_t useRewriteMask = 0;
|
||||
uint32_t outRewriteMask = 0;
|
||||
|
||||
if (opRwInfo.consecutiveLeadCount()) {
|
||||
// There must be a single consecutive register lead, otherwise the RW data is invalid.
|
||||
if (consecutiveOffset != 0xFFFFFFFFu)
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
// A consecutive lead register cannot be used as a consecutive +1/+2/+3 register, the registers must be distinct.
|
||||
if (RATiedReg::consecutiveDataFromFlags(flags) != 0)
|
||||
return DebugUtils::errored(kErrorNotConsecutiveRegs);
|
||||
|
||||
flags |= RATiedFlags::kLeadConsecutive | RATiedReg::consecutiveDataToFlags(opRwInfo.consecutiveLeadCount() - 1);
|
||||
consecutiveOffset = 0;
|
||||
|
||||
RegMask filter = raConsecutiveLeadCountToRegMaskFilter[opRwInfo.consecutiveLeadCount()];
|
||||
if (Support::test(flags, RATiedFlags::kUse)) {
|
||||
flags |= RATiedFlags::kUseConsecutive;
|
||||
useRegs &= filter;
|
||||
}
|
||||
else {
|
||||
flags |= RATiedFlags::kOutConsecutive;
|
||||
outRegs &= filter;
|
||||
}
|
||||
}
|
||||
|
||||
if (Support::test(flags, RATiedFlags::kUse)) {
|
||||
useRewriteMask = Support::bitMask(inst->getRewriteIndex(®._baseId));
|
||||
if (opRwInfo.hasOpFlag(OpRWFlags::kRegPhysId)) {
|
||||
useId = opRwInfo.physId();
|
||||
flags |= RATiedFlags::kUseFixed;
|
||||
}
|
||||
else if (opRwInfo.hasOpFlag(OpRWFlags::kConsecutive)) {
|
||||
if (consecutiveOffset == 0xFFFFFFFFu)
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
flags |= RATiedFlags::kUseConsecutive | RATiedReg::consecutiveDataToFlags(++consecutiveOffset);
|
||||
}
|
||||
}
|
||||
else {
|
||||
outRewriteMask = Support::bitMask(inst->getRewriteIndex(®._baseId));
|
||||
if (opRwInfo.hasOpFlag(OpRWFlags::kRegPhysId)) {
|
||||
outId = opRwInfo.physId();
|
||||
flags |= RATiedFlags::kOutFixed;
|
||||
}
|
||||
else if (opRwInfo.hasOpFlag(OpRWFlags::kConsecutive)) {
|
||||
if (consecutiveOffset == 0xFFFFFFFFu)
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
flags |= RATiedFlags::kOutConsecutive | RATiedReg::consecutiveDataToFlags(++consecutiveOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Special cases regarding element access.
|
||||
if (reg.as<Vec>().hasElementIndex()) {
|
||||
// Only the first 0..15 registers can be used if the register uses
|
||||
// element accessor that accesses half-words (h[0..7] elements).
|
||||
if (instInfo.hasFlag(InstDB::kInstFlagVH0_15) && reg.as<Vec>().elementType() == Vec::kElementTypeH) {
|
||||
if (Support::test(flags, RATiedFlags::kUse))
|
||||
useId &= 0x0000FFFFu;
|
||||
else
|
||||
outId &= 0x0000FFFFu;
|
||||
}
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(ib.add(workReg, flags, useRegs, useId, useRewriteMask, outRegs, outId, outRewriteMask, opRwInfo.rmSize(), consecutiveParent));
|
||||
if (singleRegOps == i)
|
||||
singleRegOps++;
|
||||
|
||||
if (Support::test(flags, RATiedFlags::kLeadConsecutive | RATiedFlags::kUseConsecutive | RATiedFlags::kOutConsecutive))
|
||||
consecutiveParent = workReg->workId();
|
||||
}
|
||||
}
|
||||
else if (op.isMem()) {
|
||||
// Memory Operand
|
||||
// --------------
|
||||
const Mem& mem = op.as<Mem>();
|
||||
|
||||
if (mem.isRegHome()) {
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(mem.baseId()), &workReg));
|
||||
_pass->getOrCreateStackSlot(workReg);
|
||||
}
|
||||
else if (mem.hasBaseReg()) {
|
||||
uint32_t vIndex = Operand::virtIdToIndex(mem.baseId());
|
||||
if (vIndex < Operand::kVirtIdCount) {
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
|
||||
|
||||
RATiedFlags flags = raMemBaseRwFlags(opRwInfo.opFlags());
|
||||
RegGroup group = workReg->group();
|
||||
RegMask allocable = _pass->_availableRegs[group];
|
||||
|
||||
// Base registers have never fixed id on ARM.
|
||||
const uint32_t useId = BaseReg::kIdBad;
|
||||
const uint32_t outId = BaseReg::kIdBad;
|
||||
|
||||
uint32_t useRewriteMask = 0;
|
||||
uint32_t outRewriteMask = 0;
|
||||
|
||||
if (Support::test(flags, RATiedFlags::kUse))
|
||||
useRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._baseId));
|
||||
else
|
||||
outRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._baseId));
|
||||
|
||||
ASMJIT_PROPAGATE(ib.add(workReg, flags, allocable, useId, useRewriteMask, allocable, outId, outRewriteMask));
|
||||
}
|
||||
}
|
||||
|
||||
if (mem.hasIndexReg()) {
|
||||
uint32_t vIndex = Operand::virtIdToIndex(mem.indexId());
|
||||
if (vIndex < Operand::kVirtIdCount) {
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
|
||||
|
||||
RATiedFlags flags = raMemIndexRwFlags(opRwInfo.opFlags());
|
||||
RegGroup group = workReg->group();
|
||||
RegMask allocable = _pass->_availableRegs[group];
|
||||
|
||||
// Index registers have never fixed id on ARM.
|
||||
const uint32_t useId = BaseReg::kIdBad;
|
||||
const uint32_t outId = BaseReg::kIdBad;
|
||||
|
||||
uint32_t useRewriteMask = 0;
|
||||
uint32_t outRewriteMask = 0;
|
||||
|
||||
if (Support::test(flags, RATiedFlags::kUse))
|
||||
useRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._data[Operand::kDataMemIndexId]));
|
||||
else
|
||||
outRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._data[Operand::kDataMemIndexId]));
|
||||
|
||||
ASMJIT_PROPAGATE(ib.add(workReg, RATiedFlags::kUse | RATiedFlags::kRead, allocable, useId, useRewriteMask, allocable, outId, outRewriteMask));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
controlType = getControlFlowType(instId);
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// a64::RACFGBuilder - OnInvoke
|
||||
// ============================
|
||||
|
||||
Error RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept {
|
||||
const FuncDetail& fd = invokeNode->detail();
|
||||
uint32_t argCount = invokeNode->argCount();
|
||||
|
||||
cc()->_setCursor(invokeNode->prev());
|
||||
|
||||
for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
|
||||
const FuncValuePack& argPack = fd.argPack(argIndex);
|
||||
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
|
||||
if (!argPack[valueIndex])
|
||||
break;
|
||||
|
||||
const FuncValue& arg = argPack[valueIndex];
|
||||
const Operand& op = invokeNode->arg(argIndex, valueIndex);
|
||||
|
||||
if (op.isNone())
|
||||
continue;
|
||||
|
||||
if (op.isReg()) {
|
||||
const Reg& reg = op.as<Reg>();
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
|
||||
|
||||
if (arg.isReg()) {
|
||||
RegGroup regGroup = workReg->group();
|
||||
RegGroup argGroup = Reg::groupOf(arg.regType());
|
||||
|
||||
if (regGroup != argGroup) {
|
||||
// TODO: [ARM] Conversion is not supported.
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(moveRegToStackArg(invokeNode, arg, reg));
|
||||
}
|
||||
}
|
||||
else if (op.isImm()) {
|
||||
if (arg.isReg()) {
|
||||
BaseReg reg;
|
||||
ASMJIT_PROPAGATE(moveImmToRegArg(invokeNode, arg, op.as<Imm>(), ®));
|
||||
invokeNode->_args[argIndex][valueIndex] = reg;
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(moveImmToStackArg(invokeNode, arg, op.as<Imm>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cc()->_setCursor(invokeNode);
|
||||
|
||||
if (fd.hasRet()) {
|
||||
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
|
||||
const FuncValue& ret = fd.ret(valueIndex);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
const Operand& op = invokeNode->ret(valueIndex);
|
||||
if (op.isReg()) {
|
||||
const Reg& reg = op.as<Reg>();
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
|
||||
|
||||
if (ret.isReg()) {
|
||||
RegGroup regGroup = workReg->group();
|
||||
RegGroup retGroup = Reg::groupOf(ret.regType());
|
||||
|
||||
if (regGroup != retGroup) {
|
||||
// TODO: [ARM] Conversion is not supported.
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This block has function call(s).
|
||||
_curBlock->addFlags(RABlockFlags::kHasFuncCalls);
|
||||
_pass->func()->frame().addAttributes(FuncAttributes::kHasFuncCalls);
|
||||
_pass->func()->frame().updateCallStackSize(fd.argStackSize());
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept {
|
||||
uint32_t argCount = invokeNode->argCount();
|
||||
const FuncDetail& fd = invokeNode->detail();
|
||||
|
||||
for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
|
||||
const FuncValuePack& argPack = fd.argPack(argIndex);
|
||||
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
|
||||
if (!argPack[valueIndex])
|
||||
continue;
|
||||
|
||||
const FuncValue& arg = argPack[valueIndex];
|
||||
const Operand& op = invokeNode->arg(argIndex, valueIndex);
|
||||
|
||||
if (op.isNone())
|
||||
continue;
|
||||
|
||||
if (op.isReg()) {
|
||||
const Reg& reg = op.as<Reg>();
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
|
||||
|
||||
if (arg.isIndirect()) {
|
||||
RegGroup regGroup = workReg->group();
|
||||
if (regGroup != RegGroup::kGp)
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
ASMJIT_PROPAGATE(ib.addCallArg(workReg, arg.regId()));
|
||||
}
|
||||
else if (arg.isReg()) {
|
||||
RegGroup regGroup = workReg->group();
|
||||
RegGroup argGroup = Reg::groupOf(arg.regType());
|
||||
|
||||
if (regGroup == argGroup) {
|
||||
ASMJIT_PROPAGATE(ib.addCallArg(workReg, arg.regId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t retIndex = 0; retIndex < Globals::kMaxValuePack; retIndex++) {
|
||||
const FuncValue& ret = fd.ret(retIndex);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
const Operand& op = invokeNode->ret(retIndex);
|
||||
if (op.isReg()) {
|
||||
const Reg& reg = op.as<Reg>();
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
|
||||
|
||||
if (ret.isReg()) {
|
||||
RegGroup regGroup = workReg->group();
|
||||
RegGroup retGroup = Reg::groupOf(ret.regType());
|
||||
|
||||
if (regGroup == retGroup) {
|
||||
ASMJIT_PROPAGATE(ib.addCallRet(workReg, ret.regId()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup clobbered registers.
|
||||
ib._clobbered[0] = Support::lsbMask<RegMask>(_pass->_physRegCount[RegGroup(0)]) & ~fd.preservedRegs(RegGroup(0));
|
||||
ib._clobbered[1] = Support::lsbMask<RegMask>(_pass->_physRegCount[RegGroup(1)]) & ~fd.preservedRegs(RegGroup(1));
|
||||
ib._clobbered[2] = Support::lsbMask<RegMask>(_pass->_physRegCount[RegGroup(2)]) & ~fd.preservedRegs(RegGroup(2));
|
||||
ib._clobbered[3] = Support::lsbMask<RegMask>(_pass->_physRegCount[RegGroup(3)]) & ~fd.preservedRegs(RegGroup(3));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// a64::RACFGBuilder - MoveImmToRegArg
|
||||
// ===================================
|
||||
|
||||
Error RACFGBuilder::moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept {
|
||||
DebugUtils::unused(invokeNode);
|
||||
ASMJIT_ASSERT(arg.isReg());
|
||||
|
||||
Imm imm(imm_);
|
||||
TypeId typeId = TypeId::kVoid;
|
||||
|
||||
switch (arg.typeId()) {
|
||||
case TypeId::kInt8 : typeId = TypeId::kUInt64; imm.signExtend8Bits(); break;
|
||||
case TypeId::kUInt8 : typeId = TypeId::kUInt64; imm.zeroExtend8Bits(); break;
|
||||
case TypeId::kInt16 : typeId = TypeId::kUInt64; imm.signExtend16Bits(); break;
|
||||
case TypeId::kUInt16: typeId = TypeId::kUInt64; imm.zeroExtend16Bits(); break;
|
||||
case TypeId::kInt32 : typeId = TypeId::kUInt64; imm.signExtend32Bits(); break;
|
||||
case TypeId::kUInt32: typeId = TypeId::kUInt64; imm.zeroExtend32Bits(); break;
|
||||
case TypeId::kInt64 : typeId = TypeId::kUInt64; break;
|
||||
case TypeId::kUInt64: typeId = TypeId::kUInt64; break;
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(cc()->_newReg(out, typeId, nullptr));
|
||||
cc()->virtRegById(out->id())->setWeight(BaseRAPass::kCallArgWeight);
|
||||
return cc()->mov(out->as<Gp>(), imm);
|
||||
}
|
||||
|
||||
// a64::RACFGBuilder - MoveImmToStackArg
|
||||
// =====================================
|
||||
|
||||
Error RACFGBuilder::moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept {
|
||||
BaseReg reg;
|
||||
|
||||
ASMJIT_PROPAGATE(moveImmToRegArg(invokeNode, arg, imm_, ®));
|
||||
ASMJIT_PROPAGATE(moveRegToStackArg(invokeNode, arg, reg));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// a64::RACFGBuilder - MoveRegToStackArg
|
||||
// =====================================
|
||||
|
||||
Error RACFGBuilder::moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept {
|
||||
DebugUtils::unused(invokeNode);
|
||||
Mem stackPtr = ptr(_pass->_sp.as<Gp>(), arg.stackOffset());
|
||||
|
||||
if (reg.isGp())
|
||||
return cc()->str(reg.as<Gp>(), stackPtr);
|
||||
|
||||
if (reg.isVec())
|
||||
return cc()->str(reg.as<Vec>(), stackPtr);
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
// a64::RACFGBuilder - OnReg
|
||||
// =========================
|
||||
|
||||
Error RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept {
|
||||
DebugUtils::unused(funcRet);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept {
|
||||
const FuncDetail& funcDetail = _pass->func()->detail();
|
||||
const Operand* opArray = funcRet->operands();
|
||||
uint32_t opCount = funcRet->opCount();
|
||||
|
||||
for (uint32_t i = 0; i < opCount; i++) {
|
||||
const Operand& op = opArray[i];
|
||||
if (op.isNone()) continue;
|
||||
|
||||
const FuncValue& ret = funcDetail.ret(i);
|
||||
if (ASMJIT_UNLIKELY(!ret.isReg()))
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
|
||||
if (op.isReg()) {
|
||||
// Register return value.
|
||||
const Reg& reg = op.as<Reg>();
|
||||
uint32_t vIndex = Operand::virtIdToIndex(reg.id());
|
||||
|
||||
if (vIndex < Operand::kVirtIdCount) {
|
||||
RAWorkReg* workReg;
|
||||
ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
|
||||
|
||||
RegGroup group = workReg->group();
|
||||
RegMask allocable = _pass->_availableRegs[group];
|
||||
ASMJIT_PROPAGATE(ib.add(workReg, RATiedFlags::kUse | RATiedFlags::kRead, allocable, ret.regId(), 0, 0, BaseReg::kIdBad, 0));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// a64::ARMRAPass - Construction & Destruction
|
||||
// ===========================================
|
||||
|
||||
ARMRAPass::ARMRAPass() noexcept
|
||||
: BaseRAPass() { _iEmitHelper = &_emitHelper; }
|
||||
ARMRAPass::~ARMRAPass() noexcept {}
|
||||
|
||||
// a64::ARMRAPass - OnInit / OnDone
|
||||
// ================================
|
||||
|
||||
void ARMRAPass::onInit() noexcept {
|
||||
Arch arch = cc()->arch();
|
||||
|
||||
_emitHelper._emitter = _cb;
|
||||
|
||||
_archTraits = &ArchTraits::byArch(arch);
|
||||
_physRegCount.set(RegGroup::kGp, 32);
|
||||
_physRegCount.set(RegGroup::kVec, 32);
|
||||
_physRegCount.set(RegGroup::kExtraVirt2, 0);
|
||||
_physRegCount.set(RegGroup::kExtraVirt3, 0);
|
||||
_buildPhysIndex();
|
||||
|
||||
_availableRegCount = _physRegCount;
|
||||
_availableRegs[RegGroup::kGp] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kGp));
|
||||
_availableRegs[RegGroup::kVec] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kVec));
|
||||
_availableRegs[RegGroup::kExtraVirt3] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kExtraVirt2));
|
||||
_availableRegs[RegGroup::kExtraVirt3] = Support::lsbMask<uint32_t>(_physRegCount.get(RegGroup::kExtraVirt3));
|
||||
|
||||
_scratchRegIndexes[0] = uint8_t(27);
|
||||
_scratchRegIndexes[1] = uint8_t(28);
|
||||
|
||||
// The architecture specific setup makes implicitly all registers available. So
|
||||
// make unavailable all registers that are special and cannot be used in general.
|
||||
bool hasFP = _func->frame().hasPreservedFP();
|
||||
|
||||
if (hasFP)
|
||||
makeUnavailable(RegGroup::kGp, Gp::kIdFp);
|
||||
|
||||
makeUnavailable(RegGroup::kGp, Gp::kIdSp);
|
||||
makeUnavailable(RegGroup::kGp, Gp::kIdOs); // OS-specific use, usually TLS.
|
||||
|
||||
_sp = sp;
|
||||
_fp = x29;
|
||||
}
|
||||
|
||||
void ARMRAPass::onDone() noexcept {}
|
||||
|
||||
// a64::ARMRAPass - BuildCFG
|
||||
// =========================
|
||||
|
||||
Error ARMRAPass::buildCFG() noexcept {
|
||||
return RACFGBuilder(this).run();
|
||||
}
|
||||
|
||||
// a64::ARMRAPass - Rewrite
|
||||
// ========================
|
||||
|
||||
ASMJIT_FAVOR_SPEED Error ARMRAPass::_rewrite(BaseNode* first, BaseNode* stop) noexcept {
|
||||
uint32_t virtCount = cc()->_vRegArray.size();
|
||||
|
||||
BaseNode* node = first;
|
||||
while (node != stop) {
|
||||
BaseNode* next = node->next();
|
||||
if (node->isInst()) {
|
||||
InstNode* inst = node->as<InstNode>();
|
||||
RAInst* raInst = node->passData<RAInst>();
|
||||
|
||||
Operand* operands = inst->operands();
|
||||
uint32_t opCount = inst->opCount();
|
||||
|
||||
uint32_t i;
|
||||
|
||||
// Rewrite virtual registers into physical registers.
|
||||
if (raInst) {
|
||||
// If the instruction contains pass data (raInst) then it was a subject
|
||||
// for register allocation and must be rewritten to use physical regs.
|
||||
RATiedReg* tiedRegs = raInst->tiedRegs();
|
||||
uint32_t tiedCount = raInst->tiedCount();
|
||||
|
||||
for (i = 0; i < tiedCount; i++) {
|
||||
RATiedReg* tiedReg = &tiedRegs[i];
|
||||
|
||||
Support::BitWordIterator<uint32_t> useIt(tiedReg->useRewriteMask());
|
||||
uint32_t useId = tiedReg->useId();
|
||||
while (useIt.hasNext())
|
||||
inst->rewriteIdAtIndex(useIt.next(), useId);
|
||||
|
||||
Support::BitWordIterator<uint32_t> outIt(tiedReg->outRewriteMask());
|
||||
uint32_t outId = tiedReg->outId();
|
||||
while (outIt.hasNext())
|
||||
inst->rewriteIdAtIndex(outIt.next(), outId);
|
||||
}
|
||||
|
||||
// This data is allocated by Zone passed to `runOnFunction()`, which
|
||||
// will be reset after the RA pass finishes. So reset this data to
|
||||
// prevent having a dead pointer after the RA pass is complete.
|
||||
node->resetPassData();
|
||||
|
||||
if (ASMJIT_UNLIKELY(node->type() != NodeType::kInst)) {
|
||||
// FuncRet terminates the flow, it must either be removed if the exit
|
||||
// label is next to it (optimization) or patched to an architecture
|
||||
// dependent jump instruction that jumps to the function's exit before
|
||||
// the epilog.
|
||||
if (node->type() == NodeType::kFuncRet) {
|
||||
RABlock* block = raInst->block();
|
||||
if (!isNextTo(node, _func->exitNode())) {
|
||||
cc()->_setCursor(node->prev());
|
||||
ASMJIT_PROPAGATE(emitJump(_func->exitNode()->label()));
|
||||
}
|
||||
|
||||
BaseNode* prev = node->prev();
|
||||
cc()->removeNode(node);
|
||||
block->setLast(prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite stack slot addresses.
|
||||
for (i = 0; i < opCount; i++) {
|
||||
Operand& op = operands[i];
|
||||
if (op.isMem()) {
|
||||
BaseMem& mem = op.as<BaseMem>();
|
||||
if (mem.isRegHome()) {
|
||||
uint32_t virtIndex = Operand::virtIdToIndex(mem.baseId());
|
||||
if (ASMJIT_UNLIKELY(virtIndex >= virtCount))
|
||||
return DebugUtils::errored(kErrorInvalidVirtId);
|
||||
|
||||
VirtReg* virtReg = cc()->virtRegByIndex(virtIndex);
|
||||
RAWorkReg* workReg = virtReg->workReg();
|
||||
ASMJIT_ASSERT(workReg != nullptr);
|
||||
|
||||
RAStackSlot* slot = workReg->stackSlot();
|
||||
int32_t offset = slot->offset();
|
||||
|
||||
mem._setBase(_sp.type(), slot->baseRegId());
|
||||
mem.clearRegHome();
|
||||
mem.addOffsetLo32(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite `loadAddressOf()` construct.
|
||||
if (inst->realId() == Inst::kIdAdr && inst->opCount() == 2 && inst->op(1).isMem()) {
|
||||
BaseMem mem = inst->op(1).as<BaseMem>();
|
||||
int64_t offset = mem.offset();
|
||||
|
||||
if (!mem.hasBaseOrIndex()) {
|
||||
inst->setId(Inst::kIdMov);
|
||||
inst->setOp(1, Imm(offset));
|
||||
}
|
||||
else {
|
||||
if (mem.hasIndex())
|
||||
return DebugUtils::errored(kErrorInvalidAddressIndex);
|
||||
|
||||
GpX dst(inst->op(0).as<Gp>().id());
|
||||
GpX base(mem.baseId());
|
||||
|
||||
InstId arithInstId = offset < 0 ? Inst::kIdSub : Inst::kIdAdd;
|
||||
uint64_t absOffset = offset < 0 ? Support::neg(uint64_t(offset)) : uint64_t(offset);
|
||||
|
||||
inst->setId(arithInstId);
|
||||
inst->setOpCount(3);
|
||||
inst->setOp(1, base);
|
||||
inst->setOp(2, Imm(absOffset));
|
||||
|
||||
// Use two operations if the offset cannot be encoded with ADD/SUB.
|
||||
if (absOffset > 0xFFFu && (absOffset & ~uint64_t(0xFFF000u)) != 0) {
|
||||
if (absOffset <= 0xFFFFFFu) {
|
||||
cc()->_setCursor(inst->prev());
|
||||
ASMJIT_PROPAGATE(cc()->emit(arithInstId, dst, base, Imm(absOffset & 0xFFFu)));
|
||||
|
||||
inst->setOp(1, dst);
|
||||
inst->setOp(2, Imm(absOffset & 0xFFF000u));
|
||||
}
|
||||
else {
|
||||
cc()->_setCursor(inst->prev());
|
||||
ASMJIT_PROPAGATE(cc()->emit(Inst::kIdMov, inst->op(0), Imm(absOffset)));
|
||||
|
||||
inst->setOp(1, base);
|
||||
inst->setOp(2, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// a64::ARMRAPass - Prolog & Epilog
|
||||
// ================================
|
||||
|
||||
Error ARMRAPass::updateStackFrame() noexcept {
|
||||
if (_func->frame().hasFuncCalls())
|
||||
_func->frame().addDirtyRegs(RegGroup::kGp, Support::bitMask(Gp::kIdLr));
|
||||
|
||||
return BaseRAPass::updateStackFrame();
|
||||
}
|
||||
|
||||
// a64::ARMRAPass - OnEmit
|
||||
// =======================
|
||||
|
||||
Error ARMRAPass::emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept {
|
||||
RAWorkReg* wReg = workRegById(workId);
|
||||
BaseReg dst(wReg->signature(), dstPhysId);
|
||||
BaseReg src(wReg->signature(), srcPhysId);
|
||||
|
||||
const char* comment = nullptr;
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasDiagnosticOption(DiagnosticOptions::kRAAnnotate)) {
|
||||
_tmpString.assignFormat("<MOVE> %s", workRegById(workId)->name());
|
||||
comment = _tmpString.data();
|
||||
}
|
||||
#endif
|
||||
|
||||
return _emitHelper.emitRegMove(dst, src, wReg->typeId(), comment);
|
||||
}
|
||||
|
||||
Error ARMRAPass::emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept {
|
||||
DebugUtils::unused(aWorkId, aPhysId, bWorkId, bPhysId);
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
Error ARMRAPass::emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept {
|
||||
RAWorkReg* wReg = workRegById(workId);
|
||||
BaseReg dstReg(wReg->signature(), dstPhysId);
|
||||
BaseMem srcMem(workRegAsMem(wReg));
|
||||
|
||||
const char* comment = nullptr;
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasDiagnosticOption(DiagnosticOptions::kRAAnnotate)) {
|
||||
_tmpString.assignFormat("<LOAD> %s", workRegById(workId)->name());
|
||||
comment = _tmpString.data();
|
||||
}
|
||||
#endif
|
||||
|
||||
return _emitHelper.emitRegMove(dstReg, srcMem, wReg->typeId(), comment);
|
||||
}
|
||||
|
||||
Error ARMRAPass::emitSave(uint32_t workId, uint32_t srcPhysId) noexcept {
|
||||
RAWorkReg* wReg = workRegById(workId);
|
||||
BaseMem dstMem(workRegAsMem(wReg));
|
||||
BaseReg srcReg(wReg->signature(), srcPhysId);
|
||||
|
||||
const char* comment = nullptr;
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasDiagnosticOption(DiagnosticOptions::kRAAnnotate)) {
|
||||
_tmpString.assignFormat("<SAVE> %s", workRegById(workId)->name());
|
||||
comment = _tmpString.data();
|
||||
}
|
||||
#endif
|
||||
|
||||
return _emitHelper.emitRegMove(dstMem, srcReg, wReg->typeId(), comment);
|
||||
}
|
||||
|
||||
Error ARMRAPass::emitJump(const Label& label) noexcept {
|
||||
return cc()->b(label);
|
||||
}
|
||||
|
||||
Error ARMRAPass::emitPreCall(InvokeNode* invokeNode) noexcept {
|
||||
DebugUtils::unused(invokeNode);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_AARCH64 && !ASMJIT_NO_COMPILER
|
||||
105
lib/lepton/asmjit/arm/a64rapass_p.h
Normal file
105
lib/lepton/asmjit/arm/a64rapass_p.h
Normal file
@ -0,0 +1,105 @@
|
||||
// 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_ARM_A64RAPASS_P_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64RAPASS_P_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
|
||||
#include "../core/compiler.h"
|
||||
#include "../core/rabuilders_p.h"
|
||||
#include "../core/rapass_p.h"
|
||||
#include "../arm/a64assembler.h"
|
||||
#include "../arm/a64compiler.h"
|
||||
#include "../arm/a64emithelper_p.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
//! ARM register allocation pass.
|
||||
//!
|
||||
//! Takes care of generating function prologs and epilogs, and also performs
|
||||
//! register allocation.
|
||||
class ARMRAPass : public BaseRAPass {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(ARMRAPass)
|
||||
typedef BaseRAPass Base;
|
||||
|
||||
EmitHelper _emitHelper;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ARMRAPass() noexcept;
|
||||
virtual ~ARMRAPass() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the compiler casted to `arm::Compiler`.
|
||||
inline Compiler* cc() const noexcept { return static_cast<Compiler*>(_cb); }
|
||||
|
||||
//! Returns emit helper.
|
||||
inline EmitHelper* emitHelper() noexcept { return &_emitHelper; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
void onInit() noexcept override;
|
||||
void onDone() noexcept override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name CFG
|
||||
//! \{
|
||||
|
||||
Error buildCFG() noexcept override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Rewrite
|
||||
//! \{
|
||||
|
||||
Error _rewrite(BaseNode* first, BaseNode* stop) noexcept override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Prolog & Epilog
|
||||
//! \{
|
||||
|
||||
Error updateStackFrame() noexcept override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Emit Helpers
|
||||
//! \{
|
||||
|
||||
Error emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept override;
|
||||
Error emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept override;
|
||||
|
||||
Error emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept override;
|
||||
Error emitSave(uint32_t workId, uint32_t srcPhysId) noexcept override;
|
||||
|
||||
Error emitJump(const Label& label) noexcept override;
|
||||
Error emitPreCall(InvokeNode* invokeNode) noexcept override;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_COMPILER
|
||||
#endif // ASMJIT_ARM_A64RAPASS_P_H_INCLUDED
|
||||
179
lib/lepton/asmjit/arm/a64utils.h
Normal file
179
lib/lepton/asmjit/arm/a64utils.h
Normal file
@ -0,0 +1,179 @@
|
||||
// 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_ARM_A64UTILS_H_INCLUDED
|
||||
#define ASMJIT_ARM_A64UTILS_H_INCLUDED
|
||||
|
||||
#include "../arm/a64globals.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(a64)
|
||||
|
||||
//! \addtogroup asmjit_a64
|
||||
//! \{
|
||||
|
||||
//! Public utilities and helpers for targeting AArch64 architecture.
|
||||
namespace Utils {
|
||||
|
||||
//! Decomposed fields of a logical immediate value (AArch64).
|
||||
struct LogicalImm {
|
||||
uint32_t n;
|
||||
uint32_t s;
|
||||
uint32_t r;
|
||||
};
|
||||
|
||||
//! Encodes the given `imm` value of the given `width` to a logical immediate value represented as N, S, and R fields
|
||||
//! and writes these fields to `out`.
|
||||
//!
|
||||
//! Encoding Table:
|
||||
//!
|
||||
//! ```
|
||||
//! +---+--------+--------+------+
|
||||
//! | N | ImmS | ImmR | Size |
|
||||
//! +---+--------+--------+------+
|
||||
//! | 1 | ssssss | rrrrrr | 64 |
|
||||
//! | 0 | 0sssss | .rrrrr | 32 |
|
||||
//! | 0 | 10ssss | ..rrrr | 16 |
|
||||
//! | 0 | 110sss | ...rrr | 8 |
|
||||
//! | 0 | 1110ss | ....rr | 4 |
|
||||
//! | 0 | 11110s | .....r | 2 |
|
||||
//! +---+--------+--------+------+
|
||||
//! ```
|
||||
ASMJIT_MAYBE_UNUSED
|
||||
static bool encodeLogicalImm(uint64_t imm, uint32_t width, a64::Utils::LogicalImm* out) noexcept {
|
||||
// Determine the element width, which must be 2, 4, 8, 16, 32, or 64 bits.
|
||||
do {
|
||||
width /= 2;
|
||||
uint64_t mask = (uint64_t(1) << width) - 1u;
|
||||
if ((imm & mask) != ((imm >> width) & mask)) {
|
||||
width *= 2;
|
||||
break;
|
||||
}
|
||||
} while (width > 2);
|
||||
|
||||
// Patterns of all zeros and all ones are not encodable.
|
||||
uint64_t lsbMask = Support::lsbMask<uint64_t>(width);
|
||||
imm &= lsbMask;
|
||||
|
||||
if (imm == 0 || imm == lsbMask)
|
||||
return false;
|
||||
|
||||
// Inspect the pattern and get the most important bit indexes.
|
||||
//
|
||||
// oIndex <-+ +-> zIndex
|
||||
// | |
|
||||
// |..zeros..|oCount|zCount|..ones..|
|
||||
// |000000000|111111|000000|11111111|
|
||||
|
||||
uint32_t zIndex = Support::ctz(~imm);
|
||||
uint64_t zImm = imm ^ ((uint64_t(1) << zIndex) - 1);
|
||||
uint32_t zCount = (zImm ? Support::ctz(zImm) : width) - zIndex;
|
||||
|
||||
uint32_t oIndex = zIndex + zCount;
|
||||
uint64_t oImm = ~(zImm ^ Support::lsbMask<uint64_t>(oIndex));
|
||||
uint32_t oCount = (oImm ? Support::ctz(oImm) : width) - (oIndex);
|
||||
|
||||
// Verify whether the bit-pattern is encodable.
|
||||
uint64_t mustBeZero = oImm ^ ~Support::lsbMask<uint64_t>(oIndex + oCount);
|
||||
if (mustBeZero != 0 || (zIndex > 0 && width - (oIndex + oCount) != 0))
|
||||
return false;
|
||||
|
||||
out->n = width == 64;
|
||||
out->s = (oCount + zIndex - 1) | (Support::neg(width * 2) & 0x3F);
|
||||
out->r = width - oIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Returns true if the given `imm` value is encodable as a logical immediate. The `width` argument describes the
|
||||
//! width of the operation, and must be either 32 or 64. This function can be used to test whether an immediate
|
||||
//! value can be used with AND, ANDS, BIC, BICS, EON, EOR, ORN, and ORR instruction.
|
||||
ASMJIT_MAYBE_UNUSED
|
||||
static inline bool isLogicalImm(uint64_t imm, uint32_t width) noexcept {
|
||||
LogicalImm dummy;
|
||||
return encodeLogicalImm(imm, width, &dummy);
|
||||
}
|
||||
|
||||
//! Returns true if the given `imm` value is a byte mask. Byte mask has each byte part of the value set to either
|
||||
//! 0x00 or 0xFF. Some ARM instructions accept immediates that form a byte-mask and this function can be used to
|
||||
//! verify that the immediate is encodable before using the value.
|
||||
template<typename T>
|
||||
static inline bool isByteMaskImm8(const T& imm) noexcept {
|
||||
constexpr T kMask = T(0x0101010101010101 & Support::allOnes<T>());
|
||||
return imm == (imm & kMask) * T(255);
|
||||
}
|
||||
|
||||
//! \cond
|
||||
//! A generic implementation that checjs whether a floating point value can be converted to ARM Imm8.
|
||||
template<typename T, uint32_t kNumBBits, uint32_t kNumCDEFGHBits, uint32_t kNumZeroBits>
|
||||
static inline bool isFPImm8Generic(T val) noexcept {
|
||||
constexpr uint32_t kAllBsMask = Support::lsbMask<uint32_t>(kNumBBits);
|
||||
constexpr uint32_t kB0Pattern = Support::bitMask(kNumBBits - 1);
|
||||
constexpr uint32_t kB1Pattern = kAllBsMask ^ kB0Pattern;
|
||||
|
||||
T immZ = val & Support::lsbMask<T>(kNumZeroBits);
|
||||
uint32_t immB = uint32_t(val >> (kNumZeroBits + kNumCDEFGHBits)) & kAllBsMask;
|
||||
|
||||
// ImmZ must be all zeros and ImmB must either be B0 or B1 pattern.
|
||||
return immZ == 0 && (immB == kB0Pattern || immB == kB1Pattern);
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
//! Returns true if the given half precision floating point `val` can be encoded as ARM IMM8 value, which represents
|
||||
//! a limited set of floating point immediate values, which can be used with FMOV instruction.
|
||||
//!
|
||||
//! The floating point must have bits distributed in the following way:
|
||||
//!
|
||||
//! ```
|
||||
//! [aBbbcdef|gh000000]
|
||||
//! ```
|
||||
static inline bool isFP16Imm8(uint32_t val) noexcept { return isFPImm8Generic<uint32_t, 3, 6, 6>(val); }
|
||||
|
||||
//! Returns true if the given single precision floating point `val` can be encoded as ARM IMM8 value, which represents
|
||||
//! a limited set of floating point immediate values, which can be used with FMOV instruction.
|
||||
//!
|
||||
//! The floating point must have bits distributed in the following way:
|
||||
//!
|
||||
//! ```
|
||||
//! [aBbbbbbc|defgh000|00000000|00000000]
|
||||
//! ```
|
||||
static inline bool isFP32Imm8(uint32_t val) noexcept { return isFPImm8Generic<uint32_t, 6, 6, 19>(val); }
|
||||
//! \overload
|
||||
static inline bool isFP32Imm8(float val) noexcept { return isFP32Imm8(Support::bitCast<uint32_t>(val)); }
|
||||
|
||||
//! Returns true if the given double precision floating point `val` can be encoded as ARM IMM8 value, which represents
|
||||
//! a limited set of floating point immediate values, which can be used with FMOV instruction.
|
||||
//!
|
||||
//! The floating point must have bits distributed in the following way:
|
||||
//!
|
||||
//! ```
|
||||
//! [aBbbbbbb|bbcdefgh|00000000|00000000|00000000|00000000|00000000|00000000]
|
||||
//! ```
|
||||
static inline bool isFP64Imm8(uint64_t val) noexcept { return isFPImm8Generic<uint64_t, 9, 6, 48>(val); }
|
||||
//! \overload
|
||||
static inline bool isFP64Imm8(double val) noexcept { return isFP64Imm8(Support::bitCast<uint64_t>(val)); }
|
||||
|
||||
//! \cond
|
||||
template<typename T, uint32_t kNumBBits, uint32_t kNumCDEFGHBits, uint32_t kNumZeroBits>
|
||||
static inline uint32_t encodeFPToImm8Generic(T val) noexcept {
|
||||
uint32_t bits = uint32_t(val >> kNumZeroBits);
|
||||
return ((bits >> (kNumBBits + kNumCDEFGHBits - 7)) & 0x80u) | (bits & 0x7F);
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
//! Encodes a double precision floating point value into IMM8 format.
|
||||
//!
|
||||
//! \note This function expects that `isFP64Imm8(val) == true` so it doesn't perform any checks of the value and just
|
||||
//! rearranges some bits into Imm8 order.
|
||||
static inline uint32_t encodeFP64ToImm8(uint64_t val) noexcept { return encodeFPToImm8Generic<uint64_t, 9, 6, 48>(val); }
|
||||
//! \overload
|
||||
static inline uint32_t encodeFP64ToImm8(double val) noexcept { return encodeFP64ToImm8(Support::bitCast<uint64_t>(val)); }
|
||||
|
||||
} // {Utils}
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_A64UTILS_H_INCLUDED
|
||||
|
||||
143
lib/lepton/asmjit/arm/armformatter.cpp
Normal file
143
lib/lepton/asmjit/arm/armformatter.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
// 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"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/misc_p.h"
|
||||
#include "../core/support.h"
|
||||
#include "../arm/armformatter_p.h"
|
||||
#include "../arm/armoperand.h"
|
||||
#include "../arm/a64instapi_p.h"
|
||||
#include "../arm/a64instdb_p.h"
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
#include "../core/compiler.h"
|
||||
#endif
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
|
||||
|
||||
// arm::FormatterInternal - Format Feature
|
||||
// =======================================
|
||||
|
||||
Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept {
|
||||
// @EnumStringBegin{"enum": "CpuFeatures::ARM", "output": "sFeature", "strip": "k"}@
|
||||
static const char sFeatureString[] =
|
||||
"None\0"
|
||||
"THUMB\0"
|
||||
"THUMBv2\0"
|
||||
"ARMv6\0"
|
||||
"ARMv7\0"
|
||||
"ARMv8a\0"
|
||||
"ARMv8_1a\0"
|
||||
"ARMv8_2a\0"
|
||||
"ARMv8_3a\0"
|
||||
"ARMv8_4a\0"
|
||||
"ARMv8_5a\0"
|
||||
"ARMv8_6a\0"
|
||||
"ARMv8_7a\0"
|
||||
"VFPv2\0"
|
||||
"VFPv3\0"
|
||||
"VFPv4\0"
|
||||
"VFP_D32\0"
|
||||
"AES\0"
|
||||
"ALTNZCV\0"
|
||||
"ASIMD\0"
|
||||
"BF16\0"
|
||||
"BTI\0"
|
||||
"CPUID\0"
|
||||
"CRC32\0"
|
||||
"DGH\0"
|
||||
"DIT\0"
|
||||
"DOTPROD\0"
|
||||
"EDSP\0"
|
||||
"FCMA\0"
|
||||
"FJCVTZS\0"
|
||||
"FLAGM\0"
|
||||
"FP16CONV\0"
|
||||
"FP16FML\0"
|
||||
"FP16FULL\0"
|
||||
"FRINT\0"
|
||||
"I8MM\0"
|
||||
"IDIVA\0"
|
||||
"IDIVT\0"
|
||||
"LSE\0"
|
||||
"MTE\0"
|
||||
"RCPC_IMMO\0"
|
||||
"RDM\0"
|
||||
"PMU\0"
|
||||
"PMULL\0"
|
||||
"RNG\0"
|
||||
"SB\0"
|
||||
"SHA1\0"
|
||||
"SHA2\0"
|
||||
"SHA3\0"
|
||||
"SHA512\0"
|
||||
"SM3\0"
|
||||
"SM4\0"
|
||||
"SSBS\0"
|
||||
"SVE\0"
|
||||
"SVE_BF16\0"
|
||||
"SVE_F32MM\0"
|
||||
"SVE_F64MM\0"
|
||||
"SVE_I8MM\0"
|
||||
"SVE_PMULL\0"
|
||||
"SVE2\0"
|
||||
"SVE2_AES\0"
|
||||
"SVE2_BITPERM\0"
|
||||
"SVE2_SHA3\0"
|
||||
"SVE2_SM4\0"
|
||||
"TME\0"
|
||||
"<Unknown>\0";
|
||||
|
||||
static const uint16_t sFeatureIndex[] = {
|
||||
0, 5, 11, 19, 25, 31, 38, 47, 56, 65, 74, 83, 92, 101, 107, 113, 119, 127,
|
||||
131, 139, 145, 150, 154, 160, 166, 170, 174, 182, 187, 192, 200, 206, 215,
|
||||
223, 232, 238, 243, 249, 255, 259, 263, 273, 277, 281, 287, 291, 294, 299,
|
||||
304, 309, 316, 320, 324, 329, 333, 342, 352, 362, 371, 381, 386, 395, 408,
|
||||
418, 427, 431
|
||||
};
|
||||
// @EnumStringEnd@
|
||||
|
||||
return sb.append(sFeatureString + sFeatureIndex[Support::min<uint32_t>(featureId, uint32_t(CpuFeatures::ARM::kMaxValue) + 1)]);
|
||||
}
|
||||
|
||||
// arm::FormatterInternal - Format Constants
|
||||
// =========================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatCondCode(String& sb, CondCode cc) noexcept {
|
||||
static const char condCodeData[] =
|
||||
"al\0" "na\0"
|
||||
"eq\0" "ne\0"
|
||||
"cs\0" "cc\0" "mi\0" "pl\0" "vs\0" "vc\0"
|
||||
"hi\0" "ls\0" "ge\0" "lt\0" "gt\0" "le\0"
|
||||
"<Unknown>";
|
||||
return sb.append(condCodeData + Support::min<uint32_t>(uint32_t(cc), 16u) * 3);
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatShiftOp(String& sb, ShiftOp shiftOp) noexcept {
|
||||
const char* str = "<Unknown>";
|
||||
switch (shiftOp) {
|
||||
case ShiftOp::kLSL: str = "lsl"; break;
|
||||
case ShiftOp::kLSR: str = "lsr"; break;
|
||||
case ShiftOp::kASR: str = "asr"; break;
|
||||
case ShiftOp::kROR: str = "ror"; break;
|
||||
case ShiftOp::kRRX: str = "rrx"; break;
|
||||
case ShiftOp::kMSL: str = "msl"; break;
|
||||
case ShiftOp::kUXTB: str = "uxtb"; break;
|
||||
case ShiftOp::kUXTH: str = "uxth"; break;
|
||||
case ShiftOp::kUXTW: str = "uxtw"; break;
|
||||
case ShiftOp::kUXTX: str = "uxtx"; break;
|
||||
case ShiftOp::kSXTB: str = "sxtb"; break;
|
||||
case ShiftOp::kSXTH: str = "sxth"; break;
|
||||
case ShiftOp::kSXTW: str = "sxtw"; break;
|
||||
case ShiftOp::kSXTX: str = "sxtx"; break;
|
||||
}
|
||||
return sb.append(str);
|
||||
}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_LOGGING
|
||||
44
lib/lepton/asmjit/arm/armformatter_p.h
Normal file
44
lib/lepton/asmjit/arm/armformatter_p.h
Normal file
@ -0,0 +1,44 @@
|
||||
// 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_ARM_ARMFORMATTER_P_H_INCLUDED
|
||||
#define ASMJIT_ARM_ARMFORMATTER_P_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/string.h"
|
||||
#include "../arm/armglobals.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_arm
|
||||
//! \{
|
||||
|
||||
namespace FormatterInternal {
|
||||
|
||||
Error ASMJIT_CDECL formatFeature(
|
||||
String& sb,
|
||||
uint32_t featureId) noexcept;
|
||||
|
||||
Error ASMJIT_CDECL formatCondCode(
|
||||
String& sb,
|
||||
CondCode cc) noexcept;
|
||||
|
||||
Error ASMJIT_CDECL formatShiftOp(
|
||||
String& sb,
|
||||
ShiftOp shiftOp) noexcept;
|
||||
|
||||
} // {FormatterInternal}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_LOGGING
|
||||
#endif // ASMJIT_ARM_ARMFORMATTER_P_H_INCLUDED
|
||||
21
lib/lepton/asmjit/arm/armglobals.h
Normal file
21
lib/lepton/asmjit/arm/armglobals.h
Normal file
@ -0,0 +1,21 @@
|
||||
// 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_ARM_ARMGLOBALS_H_INCLUDED
|
||||
#define ASMJIT_ARM_ARMGLOBALS_H_INCLUDED
|
||||
|
||||
#include "../core/archcommons.h"
|
||||
#include "../core/inst.h"
|
||||
|
||||
//! \namespace asmjit::arm
|
||||
//! \ingroup asmjit_arm
|
||||
//!
|
||||
//! API shared between AArch32 & AArch64 backends.
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_ARM_ARMGLOBALS_H_INCLUDED
|
||||
621
lib/lepton/asmjit/arm/armoperand.h
Normal file
621
lib/lepton/asmjit/arm/armoperand.h
Normal file
@ -0,0 +1,621 @@
|
||||
// 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_ARM_ARMOPERAND_H_INCLUDED
|
||||
#define ASMJIT_ARM_ARMOPERAND_H_INCLUDED
|
||||
|
||||
#include "../core/archtraits.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/type.h"
|
||||
#include "../arm/armglobals.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
|
||||
|
||||
//! \addtogroup asmjit_arm
|
||||
//! \{
|
||||
|
||||
class Reg;
|
||||
class Mem;
|
||||
|
||||
class Gp;
|
||||
class GpW;
|
||||
class GpX;
|
||||
|
||||
class Vec;
|
||||
class VecB;
|
||||
class VecH;
|
||||
class VecS;
|
||||
class VecD;
|
||||
class VecV;
|
||||
|
||||
//! Register traits (ARM/AArch64).
|
||||
//!
|
||||
//! Register traits 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
|
||||
//! change register types and groups without having to reorder these tables).
|
||||
template<RegType kRegType>
|
||||
struct RegTraits : public BaseRegTraits {};
|
||||
|
||||
//! \cond
|
||||
// <--------------------+-----+-------------------------+------------------------+---+---+------------------+
|
||||
// | Reg | Reg-Type | Reg-Group |Sz |Cnt| TypeId |
|
||||
// <--------------------+-----+-------------------------+------------------------+---+---+------------------+
|
||||
ASMJIT_DEFINE_REG_TRAITS(GpW , RegType::kARM_GpW , RegGroup::kGp , 4 , 32, TypeId::kInt32 );
|
||||
ASMJIT_DEFINE_REG_TRAITS(GpX , RegType::kARM_GpX , RegGroup::kGp , 8 , 32, TypeId::kInt64 );
|
||||
ASMJIT_DEFINE_REG_TRAITS(VecB , RegType::kARM_VecB , RegGroup::kVec , 1 , 32, TypeId::kVoid );
|
||||
ASMJIT_DEFINE_REG_TRAITS(VecH , RegType::kARM_VecH , RegGroup::kVec , 2 , 32, TypeId::kVoid );
|
||||
ASMJIT_DEFINE_REG_TRAITS(VecS , RegType::kARM_VecS , RegGroup::kVec , 4 , 32, TypeId::kInt32x1 );
|
||||
ASMJIT_DEFINE_REG_TRAITS(VecD , RegType::kARM_VecD , RegGroup::kVec , 8 , 32, TypeId::kInt32x2 );
|
||||
ASMJIT_DEFINE_REG_TRAITS(VecV , RegType::kARM_VecV , RegGroup::kVec , 16, 32, TypeId::kInt32x4 );
|
||||
//! \endcond
|
||||
|
||||
//! Register (ARM).
|
||||
class Reg : public BaseReg {
|
||||
public:
|
||||
ASMJIT_DEFINE_ABSTRACT_REG(Reg, BaseReg)
|
||||
|
||||
//! Gets whether the register is a `R|W` register (32-bit).
|
||||
inline constexpr bool isGpW() const noexcept { return baseSignature() == RegTraits<RegType::kARM_GpW>::kSignature; }
|
||||
//! Gets whether the register is an `X` register (64-bit).
|
||||
inline constexpr bool isGpX() const noexcept { return baseSignature() == RegTraits<RegType::kARM_GpX>::kSignature; }
|
||||
//! Gets whether the register is a VEC-B register (8-bit).
|
||||
inline constexpr bool isVecB() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecB>::kSignature; }
|
||||
//! Gets whether the register is a VEC-H register (16-bit).
|
||||
inline constexpr bool isVecH() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecH>::kSignature; }
|
||||
//! Gets whether the register is a VEC-S register (32-bit).
|
||||
inline constexpr bool isVecS() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecS>::kSignature; }
|
||||
//! Gets whether the register is a VEC-D register (64-bit).
|
||||
inline constexpr bool isVecD() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecD>::kSignature; }
|
||||
//! Gets whether the register is a VEC-Q register (128-bit).
|
||||
inline 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).
|
||||
inline 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).
|
||||
inline constexpr bool isVecV() const noexcept { return baseSignature() == RegTraits<RegType::kARM_VecV>::kSignature; }
|
||||
|
||||
template<RegType kRegType>
|
||||
inline void setRegT(uint32_t id) noexcept {
|
||||
setSignature(RegTraits<kRegType>::kSignature);
|
||||
setId(id);
|
||||
}
|
||||
|
||||
inline void setTypeAndId(RegType type, uint32_t id) noexcept {
|
||||
setSignature(signatureOf(type));
|
||||
setId(id);
|
||||
}
|
||||
|
||||
static inline RegGroup groupOf(RegType type) noexcept { return ArchTraits::byArch(Arch::kAArch64).regTypeToGroup(type); }
|
||||
static inline TypeId typeIdOf(RegType type) noexcept { return ArchTraits::byArch(Arch::kAArch64).regTypeToTypeId(type); }
|
||||
static inline OperandSignature signatureOf(RegType type) noexcept { return ArchTraits::byArch(Arch::kAArch64).regTypeToSignature(type); }
|
||||
|
||||
template<RegType kRegType>
|
||||
static inline RegGroup groupOfT() noexcept { return RegTraits<kRegType>::kGroup; }
|
||||
|
||||
template<RegType kRegType>
|
||||
static inline TypeId typeIdOfT() noexcept { return RegTraits<kRegType>::kTypeId; }
|
||||
|
||||
template<RegType kRegType>
|
||||
static inline OperandSignature signatureOfT() noexcept { return RegTraits<kRegType>::kSignature; }
|
||||
|
||||
static inline bool isGpW(const Operand_& op) noexcept { return op.as<Reg>().isGpW(); }
|
||||
static inline bool isGpX(const Operand_& op) noexcept { return op.as<Reg>().isGpX(); }
|
||||
static inline bool isVecB(const Operand_& op) noexcept { return op.as<Reg>().isVecB(); }
|
||||
static inline bool isVecH(const Operand_& op) noexcept { return op.as<Reg>().isVecH(); }
|
||||
static inline bool isVecS(const Operand_& op) noexcept { return op.as<Reg>().isVecS(); }
|
||||
static inline bool isVecD(const Operand_& op) noexcept { return op.as<Reg>().isVecD(); }
|
||||
static inline bool isVecQ(const Operand_& op) noexcept { return op.as<Reg>().isVecQ(); }
|
||||
static inline bool isVecV(const Operand_& op) noexcept { return op.as<Reg>().isVecV(); }
|
||||
|
||||
static inline bool isGpW(const Operand_& op, uint32_t id) noexcept { return isGpW(op) & (op.id() == id); }
|
||||
static inline bool isGpX(const Operand_& op, uint32_t id) noexcept { return isGpX(op) & (op.id() == id); }
|
||||
static inline bool isVecB(const Operand_& op, uint32_t id) noexcept { return isVecB(op) & (op.id() == id); }
|
||||
static inline bool isVecH(const Operand_& op, uint32_t id) noexcept { return isVecH(op) & (op.id() == id); }
|
||||
static inline bool isVecS(const Operand_& op, uint32_t id) noexcept { return isVecS(op) & (op.id() == id); }
|
||||
static inline bool isVecD(const Operand_& op, uint32_t id) noexcept { return isVecD(op) & (op.id() == id); }
|
||||
static inline bool isVecQ(const Operand_& op, uint32_t id) noexcept { return isVecQ(op) & (op.id() == id); }
|
||||
static inline bool isVecV(const Operand_& op, uint32_t id) noexcept { return isVecV(op) & (op.id() == id); }
|
||||
};
|
||||
|
||||
//! General purpose register (ARM).
|
||||
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.
|
||||
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
|
||||
};
|
||||
|
||||
inline constexpr bool isZR() const noexcept { return id() == kIdZr; }
|
||||
inline constexpr bool isSP() const noexcept { return id() == kIdSp; }
|
||||
|
||||
//! Cast this register to a 32-bit R|W.
|
||||
inline GpW w() const noexcept;
|
||||
//! Cast this register to a 64-bit X.
|
||||
inline 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 {
|
||||
// Register element type (3 bits).
|
||||
// |........|........|.XXX....|........|
|
||||
kSignatureRegElementTypeShift = 12,
|
||||
kSignatureRegElementTypeMask = 0x07 << kSignatureRegElementTypeShift,
|
||||
|
||||
// Register has element index (1 bit).
|
||||
// |........|........|X.......|........|
|
||||
kSignatureRegElementFlagShift = 15,
|
||||
kSignatureRegElementFlagMask = 0x01 << kSignatureRegElementFlagShift,
|
||||
|
||||
// Register element index (4 bits).
|
||||
// |........|....XXXX|........|........|
|
||||
kSignatureRegElementIndexShift = 16,
|
||||
kSignatureRegElementIndexMask = 0x0F << kSignatureRegElementIndexShift
|
||||
};
|
||||
|
||||
//! Element type.
|
||||
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.
|
||||
inline constexpr bool hasElementType() const noexcept { return _signature.hasField<kSignatureRegElementTypeMask>(); }
|
||||
//! Returns whether the register has element index (it's an element index access).
|
||||
inline constexpr bool hasElementIndex() const noexcept { return _signature.hasField<kSignatureRegElementFlagMask>(); }
|
||||
//! Returns whether the reggister has element type or element index (or both).
|
||||
inline constexpr bool hasElementTypeOrIndex() const noexcept { return _signature.hasField<kSignatureRegElementTypeMask | kSignatureRegElementFlagMask>(); }
|
||||
|
||||
//! Returns element type of the register.
|
||||
inline constexpr uint32_t elementType() const noexcept { return _signature.getField<kSignatureRegElementTypeMask>(); }
|
||||
//! Sets element type of the register to `elementType`.
|
||||
inline void setElementType(uint32_t elementType) noexcept { _signature.setField<kSignatureRegElementTypeMask>(elementType); }
|
||||
//! Resets element type to none.
|
||||
inline void resetElementType() noexcept { _signature.setField<kSignatureRegElementTypeMask>(0); }
|
||||
|
||||
//! Returns element index of the register.
|
||||
inline constexpr uint32_t elementIndex() const noexcept { return _signature.getField<kSignatureRegElementIndexMask>(); }
|
||||
//! Sets element index of the register to `elementType`.
|
||||
inline void setElementIndex(uint32_t elementIndex) noexcept {
|
||||
_signature |= kSignatureRegElementFlagMask;
|
||||
_signature.setField<kSignatureRegElementIndexMask>(elementIndex);
|
||||
}
|
||||
//! Resets element index of the register.
|
||||
inline void resetElementIndex() noexcept {
|
||||
_signature &= ~(kSignatureRegElementFlagMask | kSignatureRegElementIndexMask);
|
||||
}
|
||||
|
||||
inline constexpr bool isVecB8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementB); }
|
||||
inline constexpr bool isVecH4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementH); }
|
||||
inline constexpr bool isVecS2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature | kSignatureElementS); }
|
||||
inline constexpr bool isVecD1() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecD>::kSignature); }
|
||||
|
||||
inline constexpr bool isVecB16() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementB); }
|
||||
inline constexpr bool isVecH8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementH); }
|
||||
inline constexpr bool isVecS4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementS); }
|
||||
inline constexpr bool isVecD2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementD); }
|
||||
inline constexpr bool isVecB4x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementB4); }
|
||||
inline constexpr bool isVecH2x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits<RegType::kARM_VecV>::kSignature | kSignatureElementH2); }
|
||||
|
||||
//! Creates a cloned register with element access.
|
||||
inline Vec at(uint32_t elementIndex) const noexcept {
|
||||
return Vec((signature() & ~kSignatureRegElementIndexMask) | (elementIndex << kSignatureRegElementIndexShift) | kSignatureRegElementFlagMask, id());
|
||||
}
|
||||
|
||||
//! Cast this register to an 8-bit B register (scalar).
|
||||
inline VecB b() const noexcept;
|
||||
//! Cast this register to a 16-bit H register (scalar).
|
||||
inline VecH h() const noexcept;
|
||||
//! Cast this register to a 32-bit S register (scalar).
|
||||
inline VecS s() const noexcept;
|
||||
//! Cast this register to a 64-bit D register (scalar).
|
||||
inline VecD d() const noexcept;
|
||||
//! Cast this register to a 128-bit Q register (scalar).
|
||||
inline VecV q() const noexcept;
|
||||
//! Cast this register to a 128-bit V register.
|
||||
inline VecV v() const noexcept;
|
||||
|
||||
//! Cast this register to a 128-bit V.B[elementIndex] register.
|
||||
inline VecV b(uint32_t elementIndex) const noexcept;
|
||||
//! Cast this register to a 128-bit V.H[elementIndex] register.
|
||||
inline VecV h(uint32_t elementIndex) const noexcept;
|
||||
//! Cast this register to a 128-bit V.S[elementIndex] register.
|
||||
inline VecV s(uint32_t elementIndex) const noexcept;
|
||||
//! Cast this register to a 128-bit V.D[elementIndex] register.
|
||||
inline VecV d(uint32_t elementIndex) const noexcept;
|
||||
//! Cast this register to a 128-bit V.H2[elementIndex] register.
|
||||
inline VecV h2(uint32_t elementIndex) const noexcept;
|
||||
//! Cast this register to a 128-bit V.B4[elementIndex] register.
|
||||
inline VecV b4(uint32_t elementIndex) const noexcept;
|
||||
|
||||
//! Cast this register to V.8B.
|
||||
inline VecD b8() const noexcept;
|
||||
//! Cast this register to V.16B.
|
||||
inline VecV b16() const noexcept;
|
||||
//! Cast this register to V.2H.
|
||||
inline VecS h2() const noexcept;
|
||||
//! Cast this register to V.4H.
|
||||
inline VecD h4() const noexcept;
|
||||
//! Cast this register to V.8H.
|
||||
inline VecV h8() const noexcept;
|
||||
//! Cast this register to V.2S.
|
||||
inline VecD s2() const noexcept;
|
||||
//! Cast this register to V.4S.
|
||||
inline VecV s4() const noexcept;
|
||||
//! Cast this register to V.2D.
|
||||
inline VecV d2() const noexcept;
|
||||
|
||||
static inline 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>) };
|
||||
|
||||
inline GpW Gp::w() const noexcept { return GpW(id()); }
|
||||
inline GpX Gp::x() const noexcept { return GpX(id()); }
|
||||
|
||||
inline VecB Vec::b() const noexcept { return VecB(id()); }
|
||||
inline VecH Vec::h() const noexcept { return VecH(id()); }
|
||||
inline VecS Vec::s() const noexcept { return VecS(id()); }
|
||||
inline VecD Vec::d() const noexcept { return VecD(id()); }
|
||||
inline VecV Vec::q() const noexcept { return VecV(id()); }
|
||||
inline VecV Vec::v() const noexcept { return VecV(id()); }
|
||||
|
||||
inline VecV Vec::b(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeB, elementIndex), id()); }
|
||||
inline VecV Vec::h(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeH, elementIndex), id()); }
|
||||
inline VecV Vec::s(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeS, elementIndex), id()); }
|
||||
inline VecV Vec::d(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeD, elementIndex), id()); }
|
||||
inline VecV Vec::h2(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeH2, elementIndex), id()); }
|
||||
inline VecV Vec::b4(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeB4, elementIndex), id()); }
|
||||
|
||||
inline VecD Vec::b8() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementB}, id()); }
|
||||
inline VecS Vec::h2() const noexcept { return VecS(OperandSignature{VecS::kSignature | kSignatureElementH}, id()); }
|
||||
inline VecD Vec::h4() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementH}, id()); }
|
||||
inline VecD Vec::s2() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementS}, id()); }
|
||||
inline VecV Vec::b16() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementB}, id()); }
|
||||
inline VecV Vec::h8() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementH}, id()); }
|
||||
inline VecV Vec::s4() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementS}, id()); }
|
||||
inline 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 inline constexpr GpW w(uint32_t id) noexcept { return GpW(id); }
|
||||
//! Creates a 64-bit X register operand (AArch64).
|
||||
static inline constexpr GpX x(uint32_t id) noexcept { return GpX(id); }
|
||||
//! Creates a 32-bit S register operand (ARM/AArch64).
|
||||
static inline constexpr VecS s(uint32_t id) noexcept { return VecS(id); }
|
||||
//! Creates a 64-bit D register operand (ARM/AArch64).
|
||||
static inline constexpr VecD d(uint32_t id) noexcept { return VecD(id); }
|
||||
//! Creates a 1282-bit V register operand (ARM/AArch64).
|
||||
static inline constexpr VecV v(uint32_t id) noexcept { return VecV(id); }
|
||||
|
||||
#ifndef _DOXYGEN
|
||||
} // {regs}
|
||||
|
||||
// Make `arm::regs` accessible through `arm` namespace as well.
|
||||
using namespace regs;
|
||||
#endif
|
||||
|
||||
//! Memory operand (ARM).
|
||||
class Mem : public BaseMem {
|
||||
public:
|
||||
//! \cond INTERNAL
|
||||
//! Additional bits of operand's signature used by `arm::Mem`.
|
||||
enum AdditionalBits : uint32_t {
|
||||
// Index shift value (5 bits).
|
||||
// |........|.....XXX|XX......|........|
|
||||
kSignatureMemShiftValueShift = 14,
|
||||
kSignatureMemShiftValueMask = 0x1Fu << kSignatureMemShiftValueShift,
|
||||
|
||||
// Shift operation type (4 bits).
|
||||
// |........|XXXX....|........|........|
|
||||
kSignatureMemPredicateShift = 20,
|
||||
kSignatureMemPredicateMask = 0x0Fu << kSignatureMemPredicateShift
|
||||
};
|
||||
//! \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
|
||||
//! \{
|
||||
|
||||
//! Construct a default `Mem` operand, that points to [0].
|
||||
inline constexpr Mem() noexcept
|
||||
: BaseMem() {}
|
||||
|
||||
inline constexpr Mem(const Mem& other) noexcept
|
||||
: BaseMem(other) {}
|
||||
|
||||
inline explicit Mem(Globals::NoInit_) noexcept
|
||||
: BaseMem(Globals::NoInit) {}
|
||||
|
||||
inline constexpr Mem(const Signature& signature, uint32_t baseId, uint32_t indexId, int32_t offset) noexcept
|
||||
: BaseMem(signature, baseId, indexId, offset) {}
|
||||
|
||||
inline constexpr explicit Mem(const Label& base, int32_t off = 0, Signature signature = Signature{0}) noexcept
|
||||
: BaseMem(Signature::fromOpType(OperandType::kMem) |
|
||||
Signature::fromMemBaseType(RegType::kLabelTag) |
|
||||
signature, base.id(), 0, off) {}
|
||||
|
||||
inline constexpr explicit Mem(const BaseReg& base, int32_t off = 0, Signature signature = Signature{0}) noexcept
|
||||
: BaseMem(Signature::fromOpType(OperandType::kMem) |
|
||||
Signature::fromMemBaseType(base.type()) |
|
||||
signature, base.id(), 0, off) {}
|
||||
|
||||
inline constexpr Mem(const BaseReg& base, const BaseReg& index, Signature signature = Signature{0}) noexcept
|
||||
: BaseMem(Signature::fromOpType(OperandType::kMem) |
|
||||
Signature::fromMemBaseType(base.type()) |
|
||||
Signature::fromMemIndexType(index.type()) |
|
||||
signature, base.id(), index.id(), 0) {}
|
||||
|
||||
inline constexpr Mem(const BaseReg& base, const BaseReg& index, const Shift& shift, Signature signature = Signature{0}) noexcept
|
||||
: BaseMem(Signature::fromOpType(OperandType::kMem) |
|
||||
Signature::fromMemBaseType(base.type()) |
|
||||
Signature::fromMemIndexType(index.type()) |
|
||||
Signature::fromValue<kSignatureMemPredicateMask>(uint32_t(shift.op())) |
|
||||
Signature::fromValue<kSignatureMemShiftValueMask>(shift.value()) |
|
||||
signature, base.id(), index.id(), 0) {}
|
||||
|
||||
inline constexpr Mem(uint64_t base, Signature signature = Signature{0}) noexcept
|
||||
: BaseMem(Signature::fromOpType(OperandType::kMem) |
|
||||
signature, uint32_t(base >> 32), 0, int32_t(uint32_t(base & 0xFFFFFFFFu))) {}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline Mem& operator=(const Mem& other) noexcept = default;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Clone
|
||||
//! \{
|
||||
|
||||
//! Clones the memory operand.
|
||||
inline constexpr Mem clone() const noexcept { return Mem(*this); }
|
||||
|
||||
//! Gets new memory operand adjusted by `off`.
|
||||
inline Mem cloneAdjusted(int64_t off) const noexcept {
|
||||
Mem result(*this);
|
||||
result.addOffset(off);
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Clones the memory operand and makes it pre-index.
|
||||
inline Mem pre() const noexcept {
|
||||
Mem result(*this);
|
||||
result.setPredicate(kOffsetPreIndex);
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Clones the memory operand, applies a given offset `off` and makes it pre-index.
|
||||
inline Mem pre(int64_t off) const noexcept {
|
||||
Mem result(*this);
|
||||
result.setPredicate(kOffsetPreIndex);
|
||||
result.addOffset(off);
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Clones the memory operand and makes it post-index.
|
||||
inline Mem post() const noexcept {
|
||||
Mem result(*this);
|
||||
result.setPredicate(kOffsetPreIndex);
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Clones the memory operand, applies a given offset `off` and makes it post-index.
|
||||
inline Mem post(int64_t off) const noexcept {
|
||||
Mem result(*this);
|
||||
result.setPredicate(kOffsetPostIndex);
|
||||
result.addOffset(off);
|
||||
return result;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Base & Index
|
||||
//! \{
|
||||
|
||||
//! Converts memory `baseType` and `baseId` to `arm::Reg` instance.
|
||||
//!
|
||||
//! The memory must have a valid base register otherwise the result will be wrong.
|
||||
inline Reg baseReg() const noexcept { return Reg::fromTypeAndId(baseType(), baseId()); }
|
||||
|
||||
//! Converts memory `indexType` and `indexId` to `arm::Reg` instance.
|
||||
//!
|
||||
//! The memory must have a valid index register otherwise the result will be wrong.
|
||||
inline Reg indexReg() const noexcept { return Reg::fromTypeAndId(indexType(), indexId()); }
|
||||
|
||||
using BaseMem::setIndex;
|
||||
|
||||
inline void setIndex(const BaseReg& index, uint32_t shift) noexcept {
|
||||
setIndex(index);
|
||||
setShift(shift);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name ARM Specific Features
|
||||
//! \{
|
||||
|
||||
//! Gets whether the memory operand has shift (aka scale) constant.
|
||||
inline constexpr bool hasShift() const noexcept { return _signature.hasField<kSignatureMemShiftValueMask>(); }
|
||||
//! Gets the memory operand's shift (aka scale) constant.
|
||||
inline constexpr uint32_t shift() const noexcept { return _signature.getField<kSignatureMemShiftValueMask>(); }
|
||||
//! Sets the memory operand's shift (aka scale) constant.
|
||||
inline void setShift(uint32_t shift) noexcept { _signature.setField<kSignatureMemShiftValueMask>(shift); }
|
||||
//! Resets the memory operand's shift (aka scale) constant to zero.
|
||||
inline void resetShift() noexcept { _signature.setField<kSignatureMemShiftValueMask>(0); }
|
||||
|
||||
//! Gets memory predicate (shift mode or offset mode), see \ref ShiftOp and \ref OffsetMode.
|
||||
inline constexpr uint32_t predicate() const noexcept { return _signature.getField<kSignatureMemPredicateMask>(); }
|
||||
//! Sets memory predicate to `predicate`, see `Mem::ShiftOp`.
|
||||
inline void setPredicate(uint32_t predicate) noexcept { _signature.setField<kSignatureMemPredicateMask>(predicate); }
|
||||
//! Resets shift mode to LSL (default).
|
||||
inline void resetPredicate() noexcept { _signature.setField<kSignatureMemPredicateMask>(0); }
|
||||
|
||||
inline constexpr bool isFixedOffset() const noexcept { return predicate() < kOffsetPreIndex; }
|
||||
inline constexpr bool isPreOrPost() const noexcept { return predicate() >= kOffsetPreIndex; }
|
||||
inline constexpr bool isPreIndex() const noexcept { return predicate() == kOffsetPreIndex; }
|
||||
inline constexpr bool isPostIndex() const noexcept { return predicate() == kOffsetPostIndex; }
|
||||
|
||||
inline void resetToFixedOffset() noexcept { resetPredicate(); }
|
||||
inline void makePreIndex() noexcept { setPredicate(kOffsetPreIndex); }
|
||||
inline void makePostIndex() noexcept { setPredicate(kOffsetPostIndex); }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Creates `[base.reg, offset]` memory operand (offset mode).
|
||||
static inline constexpr Mem ptr(const Gp& base, int32_t offset = 0) noexcept {
|
||||
return Mem(base, offset);
|
||||
}
|
||||
|
||||
//! Creates `[base.reg, offset]!` memory operand (pre-index mode).
|
||||
static inline constexpr Mem ptr_pre(const Gp& base, int32_t offset = 0) noexcept {
|
||||
return Mem(base, offset, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPreIndex));
|
||||
}
|
||||
|
||||
//! Creates `[base.reg], offset` memory operand (post-index mode).
|
||||
static inline constexpr Mem ptr_post(const Gp& base, int32_t offset = 0) noexcept {
|
||||
return Mem(base, offset, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPostIndex));
|
||||
}
|
||||
|
||||
//! Creates `[base.reg, index]` memory operand.
|
||||
static inline constexpr Mem ptr(const Gp& base, const Gp& index) noexcept {
|
||||
return Mem(base, index);
|
||||
}
|
||||
|
||||
//! Creates `[base.reg], index` memory operand (post-index mode).
|
||||
static inline constexpr Mem ptr_post(const Gp& base, const Gp& index) noexcept {
|
||||
return Mem(base, index, OperandSignature::fromValue<Mem::kSignatureMemPredicateMask>(Mem::kOffsetPostIndex));
|
||||
}
|
||||
|
||||
//! Creates `[base.reg, index, SHIFT_OP #shift]` memory operand.
|
||||
static inline constexpr Mem ptr(const Gp& base, const Gp& index, const Shift& shift) noexcept {
|
||||
return Mem(base, index, shift);
|
||||
}
|
||||
|
||||
//! Creates `[base + offset]` memory operand.
|
||||
static inline 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 inline 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.
|
||||
//! Absolute memory operands can only be used if it's known that the PC relative offset is encodable and that it
|
||||
//! would be within the limits. Absolute address is also often output from disassemblers, so AsmJit support it so it
|
||||
//! can assemble it back.
|
||||
static inline constexpr Mem ptr(uint64_t base) noexcept { return Mem(base); }
|
||||
|
||||
//! \}
|
||||
|
||||
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
|
||||
17
lib/lepton/asmjit/asmjit-scope-begin.h
Normal file
17
lib/lepton/asmjit/asmjit-scope-begin.h
Normal file
@ -0,0 +1,17 @@
|
||||
// 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
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma push_macro("min")
|
||||
#pragma push_macro("max")
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#endif
|
||||
9
lib/lepton/asmjit/asmjit-scope-end.h
Normal file
9
lib/lepton/asmjit/asmjit-scope-end.h
Normal file
@ -0,0 +1,9 @@
|
||||
// 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
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma pop_macro("min")
|
||||
#pragma pop_macro("max")
|
||||
#endif
|
||||
33
lib/lepton/asmjit/asmjit.h
Normal file
33
lib/lepton/asmjit/asmjit.h
Normal file
@ -0,0 +1,33 @@
|
||||
// This file is part of AsmJit project <https://asmjit.com>
|
||||
//
|
||||
// SPDX-License-Identifier: Zlib
|
||||
// Official GitHub Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2021 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_ASMJIT_H_INCLUDED
|
||||
#define ASMJIT_ASMJIT_H_INCLUDED
|
||||
|
||||
#include "./core.h"
|
||||
|
||||
#ifndef ASMJIT_NO_X86
|
||||
#include "./x86.h"
|
||||
#endif
|
||||
|
||||
#endif // ASMJIT_ASMJIT_H_INCLUDED
|
||||
1861
lib/lepton/asmjit/core.h
Normal file
1861
lib/lepton/asmjit/core.h
Normal file
File diff suppressed because it is too large
Load Diff
55
lib/lepton/asmjit/core/api-build_p.h
Normal file
55
lib/lepton/asmjit/core/api-build_p.h
Normal file
@ -0,0 +1,55 @@
|
||||
// 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_API_BUILD_P_H_INCLUDED
|
||||
#define ASMJIT_CORE_API_BUILD_P_H_INCLUDED
|
||||
|
||||
#define ASMJIT_EXPORTS
|
||||
|
||||
// Only turn-off these warnings when building asmjit itself.
|
||||
#ifdef _MSC_VER
|
||||
#ifndef _CRT_SECURE_NO_DEPRECATE
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#endif
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Dependencies only required for asmjit build, but never exposed through public headers.
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "./api-config.h"
|
||||
|
||||
#if !defined(ASMJIT_BUILD_DEBUG) && defined(__GNUC__) && !defined(__clang__)
|
||||
#define ASMJIT_FAVOR_SIZE __attribute__((__optimize__("Os")))
|
||||
#define ASMJIT_FAVOR_SPEED __attribute__((__optimize__("O3")))
|
||||
#elif ASMJIT_CXX_HAS_ATTRIBUTE(__minsize__, 0)
|
||||
#define ASMJIT_FAVOR_SIZE __attribute__((__minsize__))
|
||||
#define ASMJIT_FAVOR_SPEED
|
||||
#else
|
||||
#define ASMJIT_FAVOR_SIZE
|
||||
#define ASMJIT_FAVOR_SPEED
|
||||
#endif
|
||||
|
||||
// Make sure '#ifdef'ed unit tests are properly highlighted in IDE.
|
||||
#if !defined(ASMJIT_TEST) && defined(__INTELLISENSE__)
|
||||
#define ASMJIT_TEST
|
||||
#endif
|
||||
|
||||
// Include a unit testing package if this is a `asmjit_test_unit` build.
|
||||
#if defined(ASMJIT_TEST)
|
||||
#include "../../../test/broken.h"
|
||||
#endif
|
||||
|
||||
#endif // ASMJIT_CORE_API_BUILD_P_H_INCLUDED
|
||||
613
lib/lepton/asmjit/core/api-config.h
Normal file
613
lib/lepton/asmjit/core/api-config.h
Normal file
@ -0,0 +1,613 @@
|
||||
// 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_API_CONFIG_H_INCLUDED
|
||||
#define ASMJIT_CORE_API_CONFIG_H_INCLUDED
|
||||
|
||||
// AsmJit Library & ABI Version
|
||||
// ============================
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
//! AsmJit library version in `(Major << 16) | (Minor << 8) | (Patch)` format.
|
||||
#define ASMJIT_LIBRARY_VERSION 0x010900 /* 1.9.0 */
|
||||
|
||||
//! \def ASMJIT_ABI_NAMESPACE
|
||||
//!
|
||||
//! AsmJit ABI namespace is an inline namespace within \ref asmjit namespace.
|
||||
//!
|
||||
//! It's used to make sure that when user links to an incompatible version of AsmJit, it won't link. It has also some
|
||||
//! additional properties as well. When `ASMJIT_ABI_NAMESPACE` is defined by the user it would override the AsmJit
|
||||
//! default, which makes it possible to use use multiple AsmJit libraries within a single project, totally controlled
|
||||
//! by the users. This is useful especially in cases in which some of such library comes from a third party.
|
||||
#ifndef ASMJIT_ABI_NAMESPACE
|
||||
#define ASMJIT_ABI_NAMESPACE _abi_1_9
|
||||
#endif
|
||||
|
||||
//! \}
|
||||
|
||||
// Global Dependencies
|
||||
// ===================
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h> // We really want std types as globals, not under 'std' namespace.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
// Build Options
|
||||
// =============
|
||||
|
||||
// NOTE: Doxygen cannot document macros that are not defined, that's why we have to define them and then undefine
|
||||
// them immediately, so it won't use the macros with its own preprocessor.
|
||||
#ifdef _DOXYGEN
|
||||
namespace asmjit {
|
||||
|
||||
//! \addtogroup asmjit_build
|
||||
//! \{
|
||||
|
||||
//! Asmjit is embedded, implies \ref ASMJIT_STATIC.
|
||||
#define ASMJIT_EMBED
|
||||
|
||||
//! Enables static-library build.
|
||||
#define ASMJIT_STATIC
|
||||
|
||||
//! Defined when AsmJit's build configuration is 'Debug'.
|
||||
//!
|
||||
//! \note Can be defined explicitly to bypass autodetection.
|
||||
#define ASMJIT_BUILD_DEBUG
|
||||
|
||||
//! Defined when AsmJit's build configuration is 'Release'.
|
||||
//!
|
||||
//! \note Can be defined explicitly to bypass autodetection.
|
||||
#define ASMJIT_BUILD_RELEASE
|
||||
|
||||
//! Disables X86/X64 backends.
|
||||
#define ASMJIT_NO_X86
|
||||
|
||||
//! Disables AArch32 backends (both ARM and Thumb).
|
||||
#define ASMJIT_NO_AARCH32
|
||||
|
||||
//! Disables AArch64 backend.
|
||||
#define ASMJIT_NO_AARCH64
|
||||
|
||||
//! Disables non-host backends entirely (useful for JIT compilers to minimize the library size).
|
||||
#define ASMJIT_NO_FOREIGN
|
||||
|
||||
//! Disables deprecated API at compile time (deprecated API won't be available).
|
||||
#define ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! Disables \ref asmjit_builder functionality completely.
|
||||
#define ASMJIT_NO_BUILDER
|
||||
|
||||
//! Disables \ref asmjit_compiler functionality completely.
|
||||
#define ASMJIT_NO_COMPILER
|
||||
|
||||
//! Disables JIT memory management and \ref asmjit::JitRuntime.
|
||||
#define ASMJIT_NO_JIT
|
||||
|
||||
//! Disables \ref asmjit::Logger and \ref asmjit::Formatter.
|
||||
#define ASMJIT_NO_LOGGING
|
||||
|
||||
//! Disables everything that contains text.
|
||||
#define ASMJIT_NO_TEXT
|
||||
|
||||
//! Disables instruction validation API.
|
||||
#define ASMJIT_NO_VALIDATION
|
||||
|
||||
//! Disables instruction introspection API.
|
||||
#define ASMJIT_NO_INTROSPECTION
|
||||
|
||||
// Avoid doxygen preprocessor using feature-selection definitions.
|
||||
#undef ASMJIT_BUILD_EMBNED
|
||||
#undef ASMJIT_BUILD_STATIC
|
||||
#undef ASMJIT_BUILD_DEBUG
|
||||
#undef ASMJIT_BUILD_RELEASE
|
||||
#undef ASMJIT_NO_X86
|
||||
#undef ASMJIT_NO_FOREIGN
|
||||
// (keep ASMJIT_NO_DEPRECATED defined, we don't document deprecated APIs).
|
||||
#undef ASMJIT_NO_BUILDER
|
||||
#undef ASMJIT_NO_COMPILER
|
||||
#undef ASMJIT_NO_JIT
|
||||
#undef ASMJIT_NO_LOGGING
|
||||
#undef ASMJIT_NO_TEXT
|
||||
#undef ASMJIT_NO_VALIDATION
|
||||
#undef ASMJIT_NO_INTROSPECTION
|
||||
|
||||
//! \}
|
||||
|
||||
} // {asmjit}
|
||||
#endif // _DOXYGEN
|
||||
|
||||
// ASMJIT_NO_BUILDER implies ASMJIT_NO_COMPILER.
|
||||
#if defined(ASMJIT_NO_BUILDER) && !defined(ASMJIT_NO_COMPILER)
|
||||
#define ASMJIT_NO_COMPILER
|
||||
#endif
|
||||
|
||||
// Prevent compile-time errors caused by misconfiguration.
|
||||
#if defined(ASMJIT_NO_TEXT) && !defined(ASMJIT_NO_LOGGING)
|
||||
#pragma message("'ASMJIT_NO_TEXT' can only be defined when 'ASMJIT_NO_LOGGING' is defined.")
|
||||
#undef ASMJIT_NO_TEXT
|
||||
#endif
|
||||
|
||||
#if defined(ASMJIT_NO_INTROSPECTION) && !defined(ASMJIT_NO_COMPILER)
|
||||
#pragma message("'ASMJIT_NO_INTROSPECTION' can only be defined when 'ASMJIT_NO_COMPILER' is defined")
|
||||
#undef ASMJIT_NO_INTROSPECTION
|
||||
#endif
|
||||
|
||||
// Build Mode
|
||||
// ==========
|
||||
|
||||
// Detect ASMJIT_BUILD_DEBUG and ASMJIT_BUILD_RELEASE if not defined.
|
||||
#if !defined(ASMJIT_BUILD_DEBUG) && !defined(ASMJIT_BUILD_RELEASE)
|
||||
#if !defined(NDEBUG)
|
||||
#define ASMJIT_BUILD_DEBUG
|
||||
#else
|
||||
#define ASMJIT_BUILD_RELEASE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Target Architecture Detection
|
||||
// =============================
|
||||
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
#define ASMJIT_ARCH_X86 64
|
||||
#elif defined(_M_IX86) || defined(__X86__) || defined(__i386__)
|
||||
#define ASMJIT_ARCH_X86 32
|
||||
#else
|
||||
#define ASMJIT_ARCH_X86 0
|
||||
#endif
|
||||
|
||||
#if defined(__arm64__) || defined(__aarch64__)
|
||||
# define ASMJIT_ARCH_ARM 64
|
||||
#elif defined(_M_ARM) || defined(_M_ARMT) || defined(__arm__) || defined(__thumb__) || defined(__thumb2__)
|
||||
#define ASMJIT_ARCH_ARM 32
|
||||
#else
|
||||
#define ASMJIT_ARCH_ARM 0
|
||||
#endif
|
||||
|
||||
#if defined(_MIPS_ARCH_MIPS64) || defined(__mips64)
|
||||
#define ASMJIT_ARCH_MIPS 64
|
||||
#elif defined(_MIPS_ARCH_MIPS32) || defined(_M_MRX000) || defined(__mips__)
|
||||
#define ASMJIT_ARCH_MIPS 32
|
||||
#else
|
||||
#define ASMJIT_ARCH_MIPS 0
|
||||
#endif
|
||||
|
||||
#define ASMJIT_ARCH_BITS (ASMJIT_ARCH_X86 | ASMJIT_ARCH_ARM | ASMJIT_ARCH_MIPS)
|
||||
#if ASMJIT_ARCH_BITS == 0
|
||||
#undef ASMJIT_ARCH_BITS
|
||||
#if defined (__LP64__) || defined(_LP64)
|
||||
#define ASMJIT_ARCH_BITS 64
|
||||
#else
|
||||
#define ASMJIT_ARCH_BITS 32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(__ARMEB__)) || \
|
||||
(defined(__MIPSEB__)) || \
|
||||
(defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
#define ASMJIT_ARCH_LE 0
|
||||
#define ASMJIT_ARCH_BE 1
|
||||
#else
|
||||
#define ASMJIT_ARCH_LE 1
|
||||
#define ASMJIT_ARCH_BE 0
|
||||
#endif
|
||||
|
||||
#if defined(ASMJIT_NO_FOREIGN)
|
||||
#if !ASMJIT_ARCH_X86 && !defined(ASMJIT_NO_X86)
|
||||
#define ASMJIT_NO_X86
|
||||
#endif
|
||||
|
||||
#if !ASMJIT_ARCH_ARM && !defined(ASMJIT_NO_AARCH64)
|
||||
#define ASMJIT_NO_AARCH64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// C++ Compiler and Features Detection
|
||||
// ===================================
|
||||
|
||||
#define ASMJIT_CXX_GNU 0
|
||||
#define ASMJIT_CXX_MAKE_VER(MAJOR, MINOR) ((MAJOR) * 1000 + (MINOR))
|
||||
|
||||
// Intel Compiler [pretends to be GNU or MSC, so it must be checked first]:
|
||||
// - https://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler
|
||||
// - https://software.intel.com/en-us/articles/c14-features-supported-by-intel-c-compiler
|
||||
// - https://software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler
|
||||
#if defined(__INTEL_COMPILER)
|
||||
|
||||
// MSC Compiler:
|
||||
// - https://msdn.microsoft.com/en-us/library/hh567368.aspx
|
||||
//
|
||||
// Version List:
|
||||
// - 16.00.0 == VS2010
|
||||
// - 17.00.0 == VS2012
|
||||
// - 18.00.0 == VS2013
|
||||
// - 19.00.0 == VS2015
|
||||
// - 19.10.0 == VS2017
|
||||
#elif defined(_MSC_VER) && defined(_MSC_FULL_VER)
|
||||
|
||||
// Clang Compiler [Pretends to be GNU, so it must be checked before]:
|
||||
// - https://clang.llvm.org/cxx_status.html
|
||||
#elif defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
|
||||
|
||||
// GNU Compiler:
|
||||
// - https://gcc.gnu.org/projects/cxx-status.html
|
||||
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
|
||||
|
||||
#undef ASMJIT_CXX_GNU
|
||||
#define ASMJIT_CXX_GNU ASMJIT_CXX_MAKE_VER(__GNUC__, __GNUC_MINOR__)
|
||||
|
||||
#endif
|
||||
|
||||
// Compiler features detection macros.
|
||||
#if defined(__clang__) && defined(__has_attribute)
|
||||
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (__has_attribute(NAME))
|
||||
#else
|
||||
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (!(!(CHECK)))
|
||||
#endif
|
||||
|
||||
// API Decorators & C++ Extensions
|
||||
// ===============================
|
||||
|
||||
//! \def ASMJIT_API
|
||||
//!
|
||||
//! A decorator that is used to decorate API that AsmJit exports when built as a shared library.
|
||||
|
||||
// API (Export / Import).
|
||||
#if !defined(ASMJIT_STATIC)
|
||||
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
|
||||
#ifdef ASMJIT_EXPORTS
|
||||
#define ASMJIT_API __declspec(dllexport)
|
||||
#else
|
||||
#define ASMJIT_API __declspec(dllimport)
|
||||
#endif
|
||||
#elif defined(_WIN32) && defined(__GNUC__)
|
||||
#ifdef ASMJIT_EXPORTS
|
||||
#define ASMJIT_API __attribute__((__dllexport__))
|
||||
#else
|
||||
#define ASMJIT_API __attribute__((__dllimport__))
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#define ASMJIT_API __attribute__((__visibility__("default")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(ASMJIT_API)
|
||||
#define ASMJIT_API
|
||||
#endif
|
||||
|
||||
#if !defined(ASMJIT_VARAPI)
|
||||
#define ASMJIT_VARAPI extern ASMJIT_API
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_VIRTAPI
|
||||
//!
|
||||
//! This is basically a workaround. When using MSVC and marking class as DLL export everything gets exported, which
|
||||
//! is unwanted in most projects. MSVC automatically exports typeinfo and vtable if at least one symbol of the class
|
||||
//! is exported. However, GCC has some strange behavior that even if one or more symbol is exported it doesn't export
|
||||
//! typeinfo unless the class itself is decorated with "visibility(default)" (i.e. ASMJIT_API).
|
||||
#if !defined(_WIN32) && defined(__GNUC__)
|
||||
#define ASMJIT_VIRTAPI ASMJIT_API
|
||||
#else
|
||||
#define ASMJIT_VIRTAPI
|
||||
#endif
|
||||
|
||||
// Function attributes.
|
||||
#if !defined(ASMJIT_BUILD_DEBUG) && defined(__GNUC__)
|
||||
#define ASMJIT_FORCE_INLINE inline __attribute__((__always_inline__))
|
||||
#elif !defined(ASMJIT_BUILD_DEBUG) && defined(_MSC_VER)
|
||||
#define ASMJIT_FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define ASMJIT_FORCE_INLINE inline
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_NOINLINE __attribute__((__noinline__))
|
||||
#define ASMJIT_NORETURN __attribute__((__noreturn__))
|
||||
#elif defined(_MSC_VER)
|
||||
#define ASMJIT_NOINLINE __declspec(noinline)
|
||||
#define ASMJIT_NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define ASMJIT_NOINLINE
|
||||
#define ASMJIT_NORETURN
|
||||
#endif
|
||||
|
||||
// Calling conventions.
|
||||
#if ASMJIT_ARCH_X86 == 32 && defined(__GNUC__)
|
||||
#define ASMJIT_CDECL __attribute__((__cdecl__))
|
||||
#define ASMJIT_STDCALL __attribute__((__stdcall__))
|
||||
#define ASMJIT_FASTCALL __attribute__((__fastcall__))
|
||||
#define ASMJIT_REGPARM(N) __attribute__((__regparm__(N)))
|
||||
#elif ASMJIT_ARCH_X86 == 32 && defined(_MSC_VER)
|
||||
#define ASMJIT_CDECL __cdecl
|
||||
#define ASMJIT_STDCALL __stdcall
|
||||
#define ASMJIT_FASTCALL __fastcall
|
||||
#define ASMJIT_REGPARM(N)
|
||||
#else
|
||||
#define ASMJIT_CDECL
|
||||
#define ASMJIT_STDCALL
|
||||
#define ASMJIT_FASTCALL
|
||||
#define ASMJIT_REGPARM(N)
|
||||
#endif
|
||||
|
||||
#if ASMJIT_ARCH_X86 && defined(_WIN32) && defined(_MSC_VER)
|
||||
#define ASMJIT_VECTORCALL __vectorcall
|
||||
#elif ASMJIT_ARCH_X86 && defined(_WIN32)
|
||||
#define ASMJIT_VECTORCALL __attribute__((__vectorcall__))
|
||||
#else
|
||||
#define ASMJIT_VECTORCALL
|
||||
#endif
|
||||
|
||||
// Type alignment (not allowed by C++11 'alignas' keyword).
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_ALIGN_TYPE(TYPE, N) __attribute__((__aligned__(N))) TYPE
|
||||
#elif defined(_MSC_VER)
|
||||
#define ASMJIT_ALIGN_TYPE(TYPE, N) __declspec(align(N)) TYPE
|
||||
#else
|
||||
#define ASMJIT_ALIGN_TYPE(TYPE, N) TYPE
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_MAY_ALIAS
|
||||
//!
|
||||
//! Expands to `__attribute__((__may_alias__))` if supported.
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_MAY_ALIAS __attribute__((__may_alias__))
|
||||
#else
|
||||
#define ASMJIT_MAY_ALIAS
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_MAYBE_UNUSED
|
||||
//!
|
||||
//! Expands to `[[maybe_unused]]` if supported or a compiler attribute instead.
|
||||
#if __cplusplus >= 201703L
|
||||
#define ASMJIT_MAYBE_UNUSED [[maybe_unused]]
|
||||
#elif defined(__GNUC__)
|
||||
#define ASMJIT_MAYBE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define ASMJIT_MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
#if defined(__clang_major__) && __clang_major__ >= 4 && !defined(_DOXYGEN)
|
||||
// NOTE: Clang allows to apply this attribute to function arguments, which is what we want. Once GCC decides to
|
||||
// support this use, we will enable it for GCC as well. However, until that, it will be clang only, which is
|
||||
// what we need for static analysis.
|
||||
#define ASMJIT_NONNULL(FUNCTION_ARGUMENT) FUNCTION_ARGUMENT __attribute__((__nonnull__))
|
||||
#else
|
||||
#define ASMJIT_NONNULL(FUNCTION_ARGUMENT) FUNCTION_ARGUMENT
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_NOEXCEPT_TYPE
|
||||
//!
|
||||
//! Defined to `noexcept` in C++17 mode or nothing otherwise. Used by function typedefs.
|
||||
#if __cplusplus >= 201703L
|
||||
#define ASMJIT_NOEXCEPT_TYPE noexcept
|
||||
#else
|
||||
#define ASMJIT_NOEXCEPT_TYPE
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_ASSUME(...)
|
||||
//!
|
||||
//! Macro that tells the C/C++ compiler that the expression `...` evaluates to true.
|
||||
//!
|
||||
//! This macro has two purposes:
|
||||
//!
|
||||
//! 1. Enable optimizations that would not be possible without the assumption.
|
||||
//! 2. Hint static analysis tools that a certain condition is true to prevent false positives.
|
||||
#if defined(__clang__)
|
||||
#define ASMJIT_ASSUME(...) __builtin_assume(__VA_ARGS__)
|
||||
#elif defined(__GNUC__)
|
||||
#define ASMJIT_ASSUME(...) do { if (!(__VA_ARGS__)) __builtin_unreachable(); } while (0)
|
||||
#elif defined(_MSC_VER)
|
||||
#define ASMJIT_ASSUME(...) __assume(__VA_ARGS__)
|
||||
#else
|
||||
#define ASMJIT_ASSUME(...) (void)0
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_LIKELY(...)
|
||||
//!
|
||||
//! Condition is likely to be taken (mostly error handling and edge cases).
|
||||
|
||||
//! \def ASMJIT_UNLIKELY(...)
|
||||
//!
|
||||
//! Condition is unlikely to be taken (mostly error handling and edge cases).
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1)
|
||||
#define ASMJIT_UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), 0)
|
||||
#else
|
||||
#define ASMJIT_LIKELY(...) (__VA_ARGS__)
|
||||
#define ASMJIT_UNLIKELY(...) (__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_FALLTHROUGH
|
||||
//!
|
||||
//! Portable [[fallthrough]] attribute.
|
||||
#if defined(__clang__) && __cplusplus >= 201103L
|
||||
#define ASMJIT_FALLTHROUGH [[clang::fallthrough]]
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 7
|
||||
#define ASMJIT_FALLTHROUGH __attribute__((__fallthrough__))
|
||||
#else
|
||||
#define ASMJIT_FALLTHROUGH ((void)0) /* fallthrough */
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_DEPRECATED
|
||||
//!
|
||||
//! Marks function, class, struct, enum, or anything else as deprecated.
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_DEPRECATED(MESSAGE) __attribute__((__deprecated__(MESSAGE)))
|
||||
#if defined(__clang__)
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) __attribute__((__deprecated__(MESSAGE)))
|
||||
#else
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) /* not usable if a deprecated function uses it */
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#define ASMJIT_DEPRECATED(MESSAGE) __declspec(deprecated(MESSAGE))
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) /* not usable if a deprecated function uses it */
|
||||
#else
|
||||
#define ASMJIT_DEPRECATED(MESSAGE)
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE)
|
||||
#endif
|
||||
|
||||
// Utilities.
|
||||
#define ASMJIT_OFFSET_OF(STRUCT, MEMBER) ((int)(intptr_t)((const char*)&((const STRUCT*)0x100)->MEMBER) - 0x100)
|
||||
#define ASMJIT_ARRAY_SIZE(X) uint32_t(sizeof(X) / sizeof(X[0]))
|
||||
|
||||
#if ASMJIT_CXX_HAS_ATTRIBUTE(no_sanitize, 0)
|
||||
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize__("undefined")))
|
||||
#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 9)
|
||||
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize_undefined__))
|
||||
#else
|
||||
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF
|
||||
#endif
|
||||
|
||||
// Begin-Namespace & End-Namespace Macros
|
||||
// ======================================
|
||||
|
||||
#if defined _DOXYGEN
|
||||
#define ASMJIT_BEGIN_NAMESPACE namespace asmjit {
|
||||
#define ASMJIT_END_NAMESPACE }
|
||||
#elif defined(__clang__)
|
||||
#define ASMJIT_BEGIN_NAMESPACE \
|
||||
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wconstant-logical-operand\"") \
|
||||
_Pragma("clang diagnostic ignored \"-Wunnamed-type-template-args\"")
|
||||
#define ASMJIT_END_NAMESPACE \
|
||||
_Pragma("clang diagnostic pop") \
|
||||
}}
|
||||
#elif defined(__GNUC__) && __GNUC__ == 4
|
||||
#define ASMJIT_BEGIN_NAMESPACE \
|
||||
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"")
|
||||
#define ASMJIT_END_NAMESPACE \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
}}
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 8
|
||||
#define ASMJIT_BEGIN_NAMESPACE \
|
||||
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"")
|
||||
#define ASMJIT_END_NAMESPACE \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
}}
|
||||
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
#define ASMJIT_BEGIN_NAMESPACE \
|
||||
namespace asmjit { inline namespace ASMJIT_ABI_NAMESPACE { \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable: 4127)) /* conditional expression is const */ \
|
||||
__pragma(warning(disable: 4201)) /* nameless struct/union */
|
||||
#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
|
||||
|
||||
// C++ Utilities
|
||||
// =============
|
||||
|
||||
#define ASMJIT_NONCOPYABLE(Type) \
|
||||
Type(const Type& other) = delete; \
|
||||
Type& operator=(const Type& other) = delete;
|
||||
|
||||
#define ASMJIT_NONCONSTRUCTIBLE(Type) \
|
||||
Type() = delete; \
|
||||
Type(const Type& other) = delete; \
|
||||
Type& operator=(const Type& other) = delete;
|
||||
|
||||
//! \def ASMJIT_DEFINE_ENUM_FLAGS(T)
|
||||
//!
|
||||
//! Defines bit operations for enumeration flags.
|
||||
#ifdef _DOXYGEN
|
||||
#define ASMJIT_DEFINE_ENUM_FLAGS(T)
|
||||
#else
|
||||
#define ASMJIT_DEFINE_ENUM_FLAGS(T) \
|
||||
static ASMJIT_FORCE_INLINE constexpr T operator~(T a) noexcept { \
|
||||
return T(~(std::underlying_type<T>::type)(a)); \
|
||||
} \
|
||||
\
|
||||
static ASMJIT_FORCE_INLINE constexpr T operator|(T a, T b) noexcept { \
|
||||
return T((std::underlying_type<T>::type)(a) | \
|
||||
(std::underlying_type<T>::type)(b)); \
|
||||
} \
|
||||
static ASMJIT_FORCE_INLINE constexpr T operator&(T a, T b) noexcept { \
|
||||
return T((std::underlying_type<T>::type)(a) & \
|
||||
(std::underlying_type<T>::type)(b)); \
|
||||
} \
|
||||
static ASMJIT_FORCE_INLINE constexpr T operator^(T a, T b) noexcept { \
|
||||
return T((std::underlying_type<T>::type)(a) ^ \
|
||||
(std::underlying_type<T>::type)(b)); \
|
||||
} \
|
||||
\
|
||||
static ASMJIT_FORCE_INLINE T& operator|=(T& a, T b) noexcept { \
|
||||
a = T((std::underlying_type<T>::type)(a) | \
|
||||
(std::underlying_type<T>::type)(b)); \
|
||||
return a; \
|
||||
} \
|
||||
static ASMJIT_FORCE_INLINE T& operator&=(T& a, T b) noexcept { \
|
||||
a = T((std::underlying_type<T>::type)(a) & \
|
||||
(std::underlying_type<T>::type)(b)); \
|
||||
return a; \
|
||||
} \
|
||||
static ASMJIT_FORCE_INLINE T& operator^=(T& a, T b) noexcept { \
|
||||
a = T((std::underlying_type<T>::type)(a) ^ \
|
||||
(std::underlying_type<T>::type)(b)); \
|
||||
return a; \
|
||||
}
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_DEFINE_ENUM_COMPARE(T)
|
||||
//!
|
||||
//! Defines comparison operations for enumeration flags.
|
||||
#ifdef _DOXYGEN
|
||||
#define ASMJIT_DEFINE_ENUM_COMPARE(T)
|
||||
#else
|
||||
#define ASMJIT_DEFINE_ENUM_COMPARE(T) \
|
||||
static ASMJIT_FORCE_INLINE bool operator<(T a, T b) noexcept { \
|
||||
return (std::underlying_type<T>::type)(a) < (std::underlying_type<T>::type)(b); \
|
||||
} \
|
||||
static ASMJIT_FORCE_INLINE bool operator<=(T a, T b) noexcept { \
|
||||
return (std::underlying_type<T>::type)(a) <= (std::underlying_type<T>::type)(b); \
|
||||
} \
|
||||
static ASMJIT_FORCE_INLINE bool operator>(T a, T b) noexcept { \
|
||||
return (std::underlying_type<T>::type)(a) > (std::underlying_type<T>::type)(b); \
|
||||
} \
|
||||
static ASMJIT_FORCE_INLINE bool operator>=(T a, T b) noexcept { \
|
||||
return (std::underlying_type<T>::type)(a) >= (std::underlying_type<T>::type)(b); \
|
||||
}
|
||||
#endif
|
||||
|
||||
// Cleanup Api-Config Specific Macros
|
||||
// ==================================
|
||||
|
||||
#undef ASMJIT_CXX_GNU
|
||||
#undef ASMJIT_CXX_MAKE_VER
|
||||
|
||||
#endif // ASMJIT_CORE_API_CONFIG_H_INCLUDED
|
||||
229
lib/lepton/asmjit/core/archcommons.h
Normal file
229
lib/lepton/asmjit/core/archcommons.h
Normal file
@ -0,0 +1,229 @@
|
||||
// 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_ARCHCOMMONS_H_INCLUDED
|
||||
#define ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED
|
||||
|
||||
// This file provides architecture-specific classes that are required in the core library. For example Imm operand
|
||||
// allows to be created from arm::Shift in a const-expr way, so the arm::Shift must be provided. So this header file
|
||||
// provides everything architecture-specific that is used by the Core API.
|
||||
|
||||
#include "../core/globals.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
|
||||
|
||||
//! \addtogroup asmjit_arm
|
||||
//! \{
|
||||
|
||||
//! Condition code (both AArch32 & AArch64).
|
||||
//!
|
||||
//! \note This enumeration doesn't match condition code that is used in AArch32/AArch64 opcodes. In general this
|
||||
//! condition code is encoded as `(cc - 2) & 0xF` so that `kAL` condition code is zero and encoded as 0xE in opcode.
|
||||
//! This makes it easier to use a condition code as an instruction modifier that defaults to 'al'.
|
||||
enum class CondCode : uint8_t {
|
||||
kAL = 0x00u, //!< (no condition code) (always)
|
||||
kNA = 0x01u, //!< (not available) (special)
|
||||
kEQ = 0x02u, //!< Z==1 (any_sign ==)
|
||||
kNE = 0x03u, //!< Z==0 (any_sign !=)
|
||||
kCS = 0x04u, //!< C==1 (unsigned >=)
|
||||
kHS = 0x04u, //!< C==1 (unsigned >=)
|
||||
kCC = 0x05u, //!< C==0 (unsigned < )
|
||||
kLO = 0x05u, //!< C==0 (unsigned < )
|
||||
kMI = 0x06u, //!< N==1 (is negative)
|
||||
kPL = 0x07u, //!< N==0 (is positive or zero)
|
||||
kVS = 0x08u, //!< V==1 (is overflow)
|
||||
kVC = 0x09u, //!< V==0 (no overflow)
|
||||
kHI = 0x0Au, //!< C==1 & Z==0 (unsigned > )
|
||||
kLS = 0x0Bu, //!< C==0 | Z==1 (unsigned <=)
|
||||
kGE = 0x0Cu, //!< N==V (signed >=)
|
||||
kLT = 0x0Du, //!< N!=V (signed < )
|
||||
kGT = 0x0Eu, //!< Z==0 & N==V (signed > )
|
||||
kLE = 0x0Fu, //!< Z==1 | N!=V (signed <=)
|
||||
|
||||
kSign = kMI, //!< Sign.
|
||||
kNotSign = kPL, //!< Not sign.
|
||||
|
||||
kOverflow = kVS, //!< Signed overflow.
|
||||
kNotOverflow = kVC, //!< Not signed overflow.
|
||||
|
||||
kEqual = kEQ, //!< Equal `a == b`.
|
||||
kNotEqual = kNE, //!< Not Equal `a != b`.
|
||||
|
||||
kZero = kEQ, //!< Zero (alias to equal).
|
||||
kNotZero = kNE, //!< Not Zero (alias to Not Equal).
|
||||
|
||||
kNegative = kMI, //!< Negative.
|
||||
kPositive = kPL, //!< Positive or zero.
|
||||
|
||||
kSignedLT = kLT, //!< Signed `a < b`.
|
||||
kSignedLE = kLE, //!< Signed `a <= b`.
|
||||
kSignedGT = kGT, //!< Signed `a > b`.
|
||||
kSignedGE = kGE, //!< Signed `a >= b`.
|
||||
|
||||
kUnsignedLT = kLO, //!< Unsigned `a < b`.
|
||||
kUnsignedLE = kLS, //!< Unsigned `a <= b`.
|
||||
kUnsignedGT = kHI, //!< Unsigned `a > b`.
|
||||
kUnsignedGE = kHS, //!< Unsigned `a >= b`.
|
||||
|
||||
kAlways = kAL, //!< No condition code (always).
|
||||
|
||||
kMaxValue = 0x0Fu //!< Maximum value of `CondCode`.
|
||||
};
|
||||
|
||||
//! Negates a condition code.
|
||||
static inline constexpr CondCode negateCond(CondCode cond) noexcept { return CondCode(uint8_t(cond) ^ uint8_t(1)); }
|
||||
|
||||
//! Data type that can be encoded with the instruction (AArch32 only).
|
||||
enum class DataType : uint32_t {
|
||||
//! No data type specified (default for all general purpose instructions).
|
||||
kNone = 0,
|
||||
//! 8-bit signed integer, specified as `.s8` in assembly.
|
||||
kS8 = 1,
|
||||
//! 16-bit signed integer, specified as `.s16` in assembly.
|
||||
kS16 = 2,
|
||||
//! 32-bit signed integer, specified as `.s32` in assembly.
|
||||
kS32 = 3,
|
||||
//! 64-bit signed integer, specified as `.s64` in assembly.
|
||||
kS64 = 4,
|
||||
//! 8-bit unsigned integer, specified as `.u8` in assembly.
|
||||
kU8 = 5,
|
||||
//! 16-bit unsigned integer, specified as `.u16` in assembly.
|
||||
kU16 = 6,
|
||||
//! 32-bit unsigned integer, specified as `.u32` in assembly.
|
||||
kU32 = 7,
|
||||
//! 64-bit unsigned integer, specified as `.u64` in assembly.
|
||||
kU64 = 8,
|
||||
//! 16-bit floating point (half precision), specified as `.f16` in assembly.
|
||||
kF16 = 10,
|
||||
//! 32-bit floating point (single precision), specified as `.f32` in assembly.
|
||||
kF32 = 11,
|
||||
//! 64-bit floating point (double precision), specified as `.f64` in assembly.
|
||||
kF64 = 12,
|
||||
//! 8-bit polynomial.
|
||||
kP8 = 13,
|
||||
//! 64-bit polynomial.
|
||||
kP64 = 15,
|
||||
|
||||
//! Maximum value of `DataType`.
|
||||
kMaxValue = 15
|
||||
};
|
||||
|
||||
//! Shift operation predicate (ARM) describes either SHIFT or EXTEND operation.
|
||||
//!
|
||||
//! \note The constants are AsmJit specific. The first 5 values describe real constants on ARM32 and AArch64 hardware,
|
||||
//! however, the addition constants that describe extend modes are specific to AsmJit and would be translated to the
|
||||
//! AArch64 specific constants by the assembler.
|
||||
enum class ShiftOp : uint32_t {
|
||||
//! Shift left logical operation (default).
|
||||
//!
|
||||
//! Available to all ARM architectures.
|
||||
kLSL = 0x00u,
|
||||
|
||||
//! Shift right logical operation.
|
||||
//!
|
||||
//! Available to all ARM architectures.
|
||||
kLSR = 0x01u,
|
||||
|
||||
//! Shift right arithmetic operation.
|
||||
//!
|
||||
//! Available to all ARM architectures.
|
||||
kASR = 0x02u,
|
||||
|
||||
//! Rotate right operation (AArch32 only).
|
||||
kROR = 0x03u,
|
||||
|
||||
//! Rotate right with carry operation (encoded as `ShiftOp::kROR` with zero) (AArch32 only).
|
||||
kRRX = 0x04u,
|
||||
|
||||
//! Shift left by filling low order bits with ones.
|
||||
kMSL = 0x05u,
|
||||
|
||||
//! UXTN extend register operation (AArch64 only).
|
||||
kUXTB = 0x06u,
|
||||
//! UXTH extend register operation (AArch64 only).
|
||||
kUXTH = 0x07u,
|
||||
//! UXTW extend register operation (AArch64 only).
|
||||
kUXTW = 0x08u,
|
||||
//! UXTX extend register operation (AArch64 only).
|
||||
kUXTX = 0x09u,
|
||||
|
||||
//! SXTB extend register operation (AArch64 only).
|
||||
kSXTB = 0x0Au,
|
||||
//! SXTH extend register operation (AArch64 only).
|
||||
kSXTH = 0x0Bu,
|
||||
//! SXTW extend register operation (AArch64 only).
|
||||
kSXTW = 0x0Cu,
|
||||
//! SXTX extend register operation (AArch64 only).
|
||||
kSXTX = 0x0Du
|
||||
|
||||
// NOTE: 0xE and 0xF are used by memory operand to specify POST|PRE offset mode.
|
||||
};
|
||||
|
||||
//! Represents ARM immediate shift operation type and value.
|
||||
class Shift {
|
||||
public:
|
||||
//! Shift operation.
|
||||
ShiftOp _op;
|
||||
//! Shift Value.
|
||||
uint32_t _value;
|
||||
|
||||
//! Default constructed Shift is not initialized.
|
||||
inline Shift() noexcept = default;
|
||||
|
||||
//! Copy constructor (default)
|
||||
constexpr Shift(const Shift& other) noexcept = default;
|
||||
|
||||
//! Constructs Shift from operation `op` and shift `value`.
|
||||
constexpr Shift(ShiftOp op, uint32_t value) noexcept
|
||||
: _op(op),
|
||||
_value(value) {}
|
||||
|
||||
//! Returns the shift operation.
|
||||
constexpr ShiftOp op() const noexcept { return _op; }
|
||||
//! Sets shift operation to `op`.
|
||||
inline void setOp(ShiftOp op) noexcept { _op = op; }
|
||||
|
||||
//! Returns the shift smount.
|
||||
constexpr uint32_t value() const noexcept { return _value; }
|
||||
//! Sets shift amount to `value`.
|
||||
inline void setValue(uint32_t value) noexcept { _value = value; }
|
||||
};
|
||||
|
||||
//! Constructs a `LSL #value` shift (logical shift left).
|
||||
static constexpr Shift lsl(uint32_t value) noexcept { return Shift(ShiftOp::kLSL, value); }
|
||||
//! Constructs a `LSR #value` shift (logical shift right).
|
||||
static constexpr Shift lsr(uint32_t value) noexcept { return Shift(ShiftOp::kLSR, value); }
|
||||
//! Constructs a `ASR #value` shift (arithmetic shift right).
|
||||
static constexpr Shift asr(uint32_t value) noexcept { return Shift(ShiftOp::kASR, value); }
|
||||
//! Constructs a `ROR #value` shift (rotate right).
|
||||
static constexpr Shift ror(uint32_t value) noexcept { return Shift(ShiftOp::kROR, value); }
|
||||
//! Constructs a `RRX` shift (rotate with carry by 1).
|
||||
static constexpr Shift rrx() noexcept { return Shift(ShiftOp::kRRX, 0); }
|
||||
//! Constructs a `MSL #value` shift (logical shift left filling ones).
|
||||
static constexpr Shift msl(uint32_t value) noexcept { return Shift(ShiftOp::kMSL, value); }
|
||||
|
||||
//! Constructs a `UXTB #value` extend and shift (unsigned byte extend).
|
||||
static constexpr Shift uxtb(uint32_t value) noexcept { return Shift(ShiftOp::kUXTB, value); }
|
||||
//! Constructs a `UXTH #value` extend and shift (unsigned hword extend).
|
||||
static constexpr Shift uxth(uint32_t value) noexcept { return Shift(ShiftOp::kUXTH, value); }
|
||||
//! Constructs a `UXTW #value` extend and shift (unsigned word extend).
|
||||
static constexpr Shift uxtw(uint32_t value) noexcept { return Shift(ShiftOp::kUXTW, value); }
|
||||
//! Constructs a `UXTX #value` extend and shift (unsigned dword extend).
|
||||
static constexpr Shift uxtx(uint32_t value) noexcept { return Shift(ShiftOp::kUXTX, value); }
|
||||
|
||||
//! Constructs a `SXTB #value` extend and shift (signed byte extend).
|
||||
static constexpr Shift sxtb(uint32_t value) noexcept { return Shift(ShiftOp::kSXTB, value); }
|
||||
//! Constructs a `SXTH #value` extend and shift (signed hword extend).
|
||||
static constexpr Shift sxth(uint32_t value) noexcept { return Shift(ShiftOp::kSXTH, value); }
|
||||
//! Constructs a `SXTW #value` extend and shift (signed word extend).
|
||||
static constexpr Shift sxtw(uint32_t value) noexcept { return Shift(ShiftOp::kSXTW, value); }
|
||||
//! Constructs a `SXTX #value` extend and shift (signed dword extend).
|
||||
static constexpr Shift sxtx(uint32_t value) noexcept { return Shift(ShiftOp::kSXTX, value); }
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED
|
||||
160
lib/lepton/asmjit/core/archtraits.cpp
Normal file
160
lib/lepton/asmjit/core/archtraits.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
// 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/archtraits.h"
|
||||
#include "../core/misc_p.h"
|
||||
|
||||
#if !defined(ASMJIT_NO_X86)
|
||||
#include "../x86/x86archtraits_p.h"
|
||||
#endif
|
||||
|
||||
#if !defined(ASMJIT_NO_AARCH64)
|
||||
#include "../arm/a64archtraits_p.h"
|
||||
#endif
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
static const constexpr ArchTraits noArchTraits = {
|
||||
// SP/FP/LR/PC.
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
|
||||
// Reserved,
|
||||
{ 0, 0, 0 },
|
||||
|
||||
// HW stack alignment.
|
||||
0,
|
||||
|
||||
// Min/Max stack offset.
|
||||
0, 0,
|
||||
|
||||
// ISA features [Gp, Vec, Other0, Other1].
|
||||
{{
|
||||
InstHints::kNoHints,
|
||||
InstHints::kNoHints,
|
||||
InstHints::kNoHints,
|
||||
InstHints::kNoHints
|
||||
}},
|
||||
|
||||
// RegTypeToSignature.
|
||||
#define V(index) OperandSignature{0}
|
||||
{{ ASMJIT_LOOKUP_TABLE_32(V, 0) }},
|
||||
#undef V
|
||||
|
||||
// RegTypeToTypeId.
|
||||
#define V(index) TypeId::kVoid
|
||||
{{ ASMJIT_LOOKUP_TABLE_32(V, 0) }},
|
||||
#undef V
|
||||
|
||||
// TypeIdToRegType.
|
||||
#define V(index) RegType::kNone
|
||||
{{ ASMJIT_LOOKUP_TABLE_32(V, 0) }},
|
||||
#undef V
|
||||
|
||||
// Word names of 8-bit, 16-bit, 32-bit, and 64-bit quantities.
|
||||
{
|
||||
ArchTypeNameId::kByte,
|
||||
ArchTypeNameId::kHalf,
|
||||
ArchTypeNameId::kWord,
|
||||
ArchTypeNameId::kQuad
|
||||
}
|
||||
};
|
||||
|
||||
ASMJIT_VARAPI const ArchTraits _archTraits[uint32_t(Arch::kMaxValue) + 1] = {
|
||||
// No architecture.
|
||||
noArchTraits,
|
||||
|
||||
// X86/X86 architectures.
|
||||
#if !defined(ASMJIT_NO_X86)
|
||||
x86::x86ArchTraits,
|
||||
x86::x64ArchTraits,
|
||||
#else
|
||||
noArchTraits,
|
||||
noArchTraits,
|
||||
#endif
|
||||
|
||||
// RISCV32/RISCV64 architectures.
|
||||
noArchTraits,
|
||||
noArchTraits,
|
||||
|
||||
// ARM architecture
|
||||
noArchTraits,
|
||||
|
||||
// AArch64 architecture.
|
||||
#if !defined(ASMJIT_NO_AARCH64)
|
||||
a64::a64ArchTraits,
|
||||
#else
|
||||
noArchTraits,
|
||||
#endif
|
||||
|
||||
// ARM/Thumb architecture.
|
||||
noArchTraits,
|
||||
|
||||
// Reserved.
|
||||
noArchTraits,
|
||||
|
||||
// MIPS32/MIPS64
|
||||
noArchTraits,
|
||||
noArchTraits
|
||||
};
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegSignature(Arch arch, TypeId typeId, TypeId* typeIdOut, OperandSignature* regSignatureOut) noexcept {
|
||||
const ArchTraits& archTraits = ArchTraits::byArch(arch);
|
||||
|
||||
// TODO: Remove this, should never be used like this.
|
||||
// Passed RegType instead of TypeId?
|
||||
if (uint32_t(typeId) <= uint32_t(RegType::kMaxValue))
|
||||
typeId = archTraits.regTypeToTypeId(RegType(uint32_t(typeId)));
|
||||
|
||||
if (ASMJIT_UNLIKELY(!TypeUtils::isValid(typeId)))
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
|
||||
// First normalize architecture dependent types.
|
||||
if (TypeUtils::isAbstract(typeId)) {
|
||||
bool is32Bit = Environment::is32Bit(arch);
|
||||
if (typeId == TypeId::kIntPtr)
|
||||
typeId = is32Bit ? TypeId::kInt32 : TypeId::kInt64;
|
||||
else
|
||||
typeId = is32Bit ? TypeId::kUInt32 : TypeId::kUInt64;
|
||||
}
|
||||
|
||||
// Type size helps to construct all groups of registers.
|
||||
// TypeId is invalid if the size is zero.
|
||||
uint32_t size = TypeUtils::sizeOf(typeId);
|
||||
if (ASMJIT_UNLIKELY(!size))
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
|
||||
if (ASMJIT_UNLIKELY(typeId == TypeId::kFloat80))
|
||||
return DebugUtils::errored(kErrorInvalidUseOfF80);
|
||||
|
||||
RegType regType = RegType::kNone;
|
||||
if (TypeUtils::isBetween(typeId, TypeId::_kBaseStart, TypeId::_kVec32Start)) {
|
||||
regType = archTraits._typeIdToRegType[uint32_t(typeId) - uint32_t(TypeId::_kBaseStart)];
|
||||
if (regType == RegType::kNone) {
|
||||
if (typeId == TypeId::kInt64 || typeId == TypeId::kUInt64)
|
||||
return DebugUtils::errored(kErrorInvalidUseOfGpq);
|
||||
else
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (size <= 8 && archTraits._regSignature[RegType::kVec64].isValid())
|
||||
regType = RegType::kVec64;
|
||||
else if (size <= 16 && archTraits._regSignature[RegType::kVec128].isValid())
|
||||
regType = RegType::kVec128;
|
||||
else if (size == 32 && archTraits._regSignature[RegType::kVec256].isValid())
|
||||
regType = RegType::kVec256;
|
||||
else if (archTraits._regSignature[RegType::kVec512].isValid())
|
||||
regType = RegType::kVec512;
|
||||
else
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
}
|
||||
|
||||
*typeIdOut = typeId;
|
||||
*regSignatureOut = archTraits.regTypeToSignature(regType);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
290
lib/lepton/asmjit/core/archtraits.h
Normal file
290
lib/lepton/asmjit/core/archtraits.h
Normal file
@ -0,0 +1,290 @@
|
||||
// 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_ARCHTRAITS_H_INCLUDED
|
||||
#define ASMJIT_CORE_ARCHTRAITS_H_INCLUDED
|
||||
|
||||
#include "../core/operand.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
//! Instruction set architecture (ISA).
|
||||
enum class Arch : uint8_t {
|
||||
//! Unknown or uninitialized ISA.
|
||||
kUnknown = 0,
|
||||
|
||||
//! 32-bit X86 ISA.
|
||||
kX86 = 1,
|
||||
//! 64-bit X86 ISA also known as X64, X86_64, and AMD64.
|
||||
kX64 = 2,
|
||||
|
||||
//! 32-bit RISC-V ISA.
|
||||
kRISCV32 = 3,
|
||||
//! 64-bit RISC-V ISA.
|
||||
kRISCV64 = 4,
|
||||
|
||||
//! 32-bit ARM ISA (little endian).
|
||||
kARM = 5,
|
||||
//! 64-bit ARM ISA in (little endian).
|
||||
kAArch64 = 6,
|
||||
//! 32-bit ARM ISA in Thumb mode (little endian).
|
||||
kThumb = 7,
|
||||
|
||||
// 8 is not used at the moment, even numbers are 64-bit architectures.
|
||||
|
||||
//! 32-bit MIPS ISA in (little endian).
|
||||
kMIPS32_LE = 9,
|
||||
//! 64-bit MIPS ISA in (little endian).
|
||||
kMIPS64_LE = 10,
|
||||
|
||||
//! 32-bit ARM ISA (big endian).
|
||||
kARM_BE = 11,
|
||||
//! 64-bit ARM ISA in (big endian).
|
||||
kAArch64_BE = 12,
|
||||
//! 32-bit ARM ISA in Thumb mode (big endian).
|
||||
kThumb_BE = 13,
|
||||
|
||||
// 14 is not used at the moment, even numbers are 64-bit architectures.
|
||||
|
||||
//! 32-bit MIPS ISA in (big endian).
|
||||
kMIPS32_BE = 15,
|
||||
//! 64-bit MIPS ISA in (big endian).
|
||||
kMIPS64_BE = 16,
|
||||
|
||||
//! Maximum value of `Arch`.
|
||||
kMaxValue = kMIPS64_BE,
|
||||
|
||||
//! Mask used by 32-bit ISAs (odd are 32-bit, even are 64-bit).
|
||||
k32BitMask = 0x01,
|
||||
//! First big-endian architecture.
|
||||
kBigEndian = kARM_BE,
|
||||
|
||||
//! ISA detected at compile-time (ISA of the host).
|
||||
kHost =
|
||||
#if defined(_DOXYGEN)
|
||||
DETECTED_AT_COMPILE_TIME
|
||||
#else
|
||||
ASMJIT_ARCH_X86 == 32 ? kX86 :
|
||||
ASMJIT_ARCH_X86 == 64 ? kX64 :
|
||||
|
||||
ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_LE ? kARM :
|
||||
ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_BE ? kARM_BE :
|
||||
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kAArch64 :
|
||||
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_BE ? kAArch64_BE :
|
||||
|
||||
ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_LE ? kMIPS32_LE :
|
||||
ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kMIPS32_BE :
|
||||
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_LE ? kMIPS64_LE :
|
||||
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_BE ? kMIPS64_BE :
|
||||
|
||||
kUnknown
|
||||
#endif
|
||||
};
|
||||
|
||||
//! Sub-architecture.
|
||||
enum class SubArch : uint8_t {
|
||||
//! Unknown or uninitialized architecture sub-type.
|
||||
kUnknown = 0,
|
||||
|
||||
//! Maximum value of `SubArch`.
|
||||
kMaxValue = kUnknown,
|
||||
|
||||
//! Sub-architecture detected at compile-time (sub-architecture of the host).
|
||||
kHost =
|
||||
#if defined(_DOXYGEN)
|
||||
DETECTED_AT_COMPILE_TIME
|
||||
#else
|
||||
kUnknown
|
||||
#endif
|
||||
};
|
||||
|
||||
//! Identifier used to represent names of different data types across architectures.
|
||||
enum class ArchTypeNameId : uint8_t {
|
||||
//! Describes 'db' (X86/X86_64 convention, always 8-bit quantity).
|
||||
kDB = 0,
|
||||
//! Describes 'dw' (X86/X86_64 convention, always 16-bit word).
|
||||
kDW,
|
||||
//! Describes 'dd' (X86/X86_64 convention, always 32-bit word).
|
||||
kDD,
|
||||
//! Describes 'dq' (X86/X86_64 convention, always 64-bit word).
|
||||
kDQ,
|
||||
//! Describes 'byte' (always 8-bit quantity).
|
||||
kByte,
|
||||
//! Describes 'half' (most likely 16-bit word).
|
||||
kHalf,
|
||||
//! Describes 'word' (either 16-bit or 32-bit word).
|
||||
kWord,
|
||||
//! Describes 'hword' (most likely 16-bit word).
|
||||
kHWord,
|
||||
//! Describes 'dword' (either 32-bit or 64-bit word).
|
||||
kDWord,
|
||||
//! Describes 'qword' (64-bit word).
|
||||
kQWord,
|
||||
//! Describes 'xword' (64-bit word).
|
||||
kXWord,
|
||||
//! Describes 'short' (always 16-bit word).
|
||||
kShort,
|
||||
//! Describes 'long' (most likely 32-bit word).
|
||||
kLong,
|
||||
//! Describes 'quad' (64-bit word).
|
||||
kQuad,
|
||||
|
||||
//! Maximum value of `ArchTypeNameId`.
|
||||
kMaxValue = kQuad
|
||||
};
|
||||
|
||||
//! Instruction feature hints for each register group provided by \ref ArchTraits.
|
||||
//!
|
||||
//! Instruction feature hints describe miscellaneous instructions provided by the architecture that can be used by
|
||||
//! register allocator to make certain things simpler - like register swaps or emitting register push/pop sequences.
|
||||
//!
|
||||
//! \remarks Instruction feature hints are only defined for register groups that can be used with \ref
|
||||
//! asmjit_compiler infrastructure. Register groups that are not managed by Compiler are not provided by
|
||||
//! \ref ArchTraits and cannot be queried.
|
||||
enum class InstHints : uint8_t {
|
||||
//! No feature hints.
|
||||
kNoHints = 0,
|
||||
|
||||
//! Architecture supports a register swap by using a single instruction.
|
||||
kRegSwap = 0x01u,
|
||||
//! Architecture provides push/pop instructions.
|
||||
kPushPop = 0x02u
|
||||
};
|
||||
ASMJIT_DEFINE_ENUM_FLAGS(InstHints)
|
||||
|
||||
//! Architecture traits used by Function API and Compiler's register allocator.
|
||||
struct ArchTraits {
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Stack pointer register id.
|
||||
uint8_t _spRegId;
|
||||
//! Frame pointer register id.
|
||||
uint8_t _fpRegId;
|
||||
//! Link register id.
|
||||
uint8_t _linkRegId;
|
||||
//! Instruction pointer (or program counter) register id, if accessible.
|
||||
uint8_t _ipRegId;
|
||||
|
||||
// Reserved.
|
||||
uint8_t _reserved[3];
|
||||
//! Hardware stack alignment requirement.
|
||||
uint8_t _hwStackAlignment;
|
||||
|
||||
//! Minimum addressable offset on stack guaranteed for all instructions.
|
||||
uint32_t _minStackOffset;
|
||||
//! Maximum addressable offset on stack depending on specific instruction.
|
||||
uint32_t _maxStackOffset;
|
||||
|
||||
//! Flags for each virtual register group.
|
||||
Support::Array<InstHints, Globals::kNumVirtGroups> _instHints;
|
||||
|
||||
//! Maps register type into a signature, that provides group, size and can be used to construct register operands.
|
||||
Support::Array<OperandSignature, uint32_t(RegType::kMaxValue) + 1> _regSignature;
|
||||
//! Maps a register to type-id, see \ref TypeId.
|
||||
Support::Array<TypeId, uint32_t(RegType::kMaxValue) + 1> _regTypeToTypeId;
|
||||
//! Maps scalar TypeId values (from TypeId::_kIdBaseStart) to register types, see \ref TypeId.
|
||||
Support::Array<RegType, 32> _typeIdToRegType;
|
||||
|
||||
//! Word name identifiers of 8-bit, 16-bit, 32-biit, and 64-bit quantities that appear in formatted text.
|
||||
ArchTypeNameId _typeNameIdTable[4];
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns stack pointer register id.
|
||||
inline uint32_t spRegId() const noexcept { return _spRegId; }
|
||||
//! Returns stack frame register id.
|
||||
inline uint32_t fpRegId() const noexcept { return _fpRegId; }
|
||||
//! Returns link register id, if the architecture provides it.
|
||||
inline uint32_t linkRegId() const noexcept { return _linkRegId; }
|
||||
//! Returns instruction pointer register id, if the architecture provides it.
|
||||
inline uint32_t ipRegId() const noexcept { return _ipRegId; }
|
||||
|
||||
//! Returns a hardware stack alignment requirement.
|
||||
//!
|
||||
//! \note This is a hardware constraint. Architectures that don't constrain it would return the lowest alignment
|
||||
//! (1), however, some architectures may constrain the alignment, for example AArch64 requires 16-byte alignment.
|
||||
inline uint32_t hwStackAlignment() const noexcept { return _hwStackAlignment; }
|
||||
|
||||
//! Tests whether the architecture provides link register, which is used across function calls. If the link
|
||||
//! register is not provided then a function call pushes the return address on stack (X86/X64).
|
||||
inline bool hasLinkReg() const noexcept { return _linkRegId != BaseReg::kIdBad; }
|
||||
|
||||
//! Returns minimum addressable offset on stack guaranteed for all instructions.
|
||||
inline uint32_t minStackOffset() const noexcept { return _minStackOffset; }
|
||||
//! Returns maximum addressable offset on stack depending on specific instruction.
|
||||
inline uint32_t maxStackOffset() const noexcept { return _maxStackOffset; }
|
||||
|
||||
//! Returns ISA flags of the given register `group`.
|
||||
inline InstHints instFeatureHints(RegGroup group) const noexcept { return _instHints[group]; }
|
||||
//! Tests whether the given register `group` has the given `flag` set.
|
||||
inline bool hasInstHint(RegGroup group, InstHints feature) const noexcept { return Support::test(_instHints[group], feature); }
|
||||
//! Tests whether the ISA provides register swap instruction for the given register `group`.
|
||||
inline bool hasInstRegSwap(RegGroup group) const noexcept { return hasInstHint(group, InstHints::kRegSwap); }
|
||||
//! Tests whether the ISA provides push/pop instructions for the given register `group`.
|
||||
inline bool hasInstPushPop(RegGroup group) const noexcept { return hasInstHint(group, InstHints::kPushPop); }
|
||||
|
||||
inline bool hasRegType(RegType type) const noexcept {
|
||||
return type <= RegType::kMaxValue && _regSignature[type].isValid();
|
||||
}
|
||||
|
||||
//! Returns an operand signature from the given register `type` of this architecture.
|
||||
inline OperandSignature regTypeToSignature(RegType type) const noexcept { return _regSignature[type]; }
|
||||
//! Returns a register from the given register `type` of this architecture.
|
||||
inline RegGroup regTypeToGroup(RegType type) const noexcept { return _regSignature[type].regGroup(); }
|
||||
//! Returns a register size the given register `type` of this architecture.
|
||||
inline uint32_t regTypeToSize(RegType type) const noexcept { return _regSignature[type].size(); }
|
||||
//! Returns a corresponding `TypeId` from the given register `type` of this architecture.
|
||||
inline TypeId regTypeToTypeId(RegType type) const noexcept { return _regTypeToTypeId[type]; }
|
||||
|
||||
//! Returns a table of ISA word names that appear in formatted text. Word names are ISA dependent.
|
||||
//!
|
||||
//! The index of this table is log2 of the size:
|
||||
//! - [0] 8-bits
|
||||
//! - [1] 16-bits
|
||||
//! - [2] 32-bits
|
||||
//! - [3] 64-bits
|
||||
inline const ArchTypeNameId* typeNameIdTable() const noexcept { return _typeNameIdTable; }
|
||||
|
||||
//! Returns an ISA word name identifier of the given `index`, see \ref typeNameIdTable() for more details.
|
||||
inline ArchTypeNameId typeNameIdByIndex(uint32_t index) const noexcept { return _typeNameIdTable[index]; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Statics
|
||||
//! \{
|
||||
|
||||
//! Returns a const reference to `ArchTraits` for the given architecture `arch`.
|
||||
static inline const ArchTraits& byArch(Arch arch) noexcept;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
ASMJIT_VARAPI const ArchTraits _archTraits[uint32_t(Arch::kMaxValue) + 1];
|
||||
|
||||
//! \cond
|
||||
inline const ArchTraits& ArchTraits::byArch(Arch arch) noexcept { return _archTraits[uint32_t(arch)]; }
|
||||
//! \endcond
|
||||
|
||||
//! Architecture utilities.
|
||||
namespace ArchUtils {
|
||||
|
||||
ASMJIT_API Error typeIdToRegSignature(Arch arch, TypeId typeId, TypeId* typeIdOut, OperandSignature* regSignatureOut) noexcept;
|
||||
|
||||
} // {ArchUtils}
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_ARCHTRAITS_H_INCLUDED
|
||||
406
lib/lepton/asmjit/core/assembler.cpp
Normal file
406
lib/lepton/asmjit/core/assembler.cpp
Normal file
@ -0,0 +1,406 @@
|
||||
// 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/assembler.h"
|
||||
#include "../core/codewriter_p.h"
|
||||
#include "../core/constpool.h"
|
||||
#include "../core/emitterutils_p.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// BaseAssembler - Construction & Destruction
|
||||
// ==========================================
|
||||
|
||||
BaseAssembler::BaseAssembler() noexcept
|
||||
: BaseEmitter(EmitterType::kAssembler) {}
|
||||
|
||||
BaseAssembler::~BaseAssembler() noexcept {}
|
||||
|
||||
// BaseAssembler - Buffer Management
|
||||
// =================================
|
||||
|
||||
Error BaseAssembler::setOffset(size_t offset) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
size_t size = Support::max<size_t>(_section->bufferSize(), this->offset());
|
||||
if (ASMJIT_UNLIKELY(offset > size))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
_bufferPtr = _bufferData + offset;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseAssembler - Section Management
|
||||
// ==================================
|
||||
|
||||
static void BaseAssembler_initSection(BaseAssembler* self, Section* section) noexcept {
|
||||
uint8_t* p = section->_buffer._data;
|
||||
|
||||
self->_section = section;
|
||||
self->_bufferData = p;
|
||||
self->_bufferPtr = p + section->_buffer._size;
|
||||
self->_bufferEnd = p + section->_buffer._capacity;
|
||||
}
|
||||
|
||||
Error BaseAssembler::section(Section* section) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
if (!_code->isSectionValid(section->id()) || _code->_sections[section->id()] != section)
|
||||
return reportError(DebugUtils::errored(kErrorInvalidSection));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger)
|
||||
_logger->logf(".section %s {#%u}\n", section->name(), section->id());
|
||||
#endif
|
||||
|
||||
BaseAssembler_initSection(this, section);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseAssembler - Label Management
|
||||
// ================================
|
||||
|
||||
Label BaseAssembler::newLabel() {
|
||||
uint32_t labelId = Globals::kInvalidId;
|
||||
if (ASMJIT_LIKELY(_code)) {
|
||||
LabelEntry* le;
|
||||
Error err = _code->newLabelEntry(&le);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
reportError(err);
|
||||
else
|
||||
labelId = le->id();
|
||||
}
|
||||
return Label(labelId);
|
||||
}
|
||||
|
||||
Label BaseAssembler::newNamedLabel(const char* name, size_t nameSize, LabelType type, uint32_t parentId) {
|
||||
uint32_t labelId = Globals::kInvalidId;
|
||||
if (ASMJIT_LIKELY(_code)) {
|
||||
LabelEntry* le;
|
||||
Error err = _code->newNamedLabelEntry(&le, name, nameSize, type, parentId);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
reportError(err);
|
||||
else
|
||||
labelId = le->id();
|
||||
}
|
||||
return Label(labelId);
|
||||
}
|
||||
|
||||
Error BaseAssembler::bind(const Label& label) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
Error err = _code->bindLabel(label, _section->id(), offset());
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger)
|
||||
EmitterUtils::logLabelBound(this, label);
|
||||
#endif
|
||||
|
||||
resetInlineComment();
|
||||
if (err)
|
||||
return reportError(err);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseAssembler - Embed
|
||||
// =====================
|
||||
|
||||
Error BaseAssembler::embed(const void* data, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
if (dataSize == 0)
|
||||
return kErrorOk;
|
||||
|
||||
CodeWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
|
||||
|
||||
writer.emitData(data, dataSize);
|
||||
writer.done(this);
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger) {
|
||||
StringTmp<512> sb;
|
||||
Formatter::formatData(sb, _logger->flags(), arch(), TypeId::kUInt8, data, dataSize, 1);
|
||||
sb.append('\n');
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedDataArray(TypeId typeId, const void* data, size_t itemCount, size_t repeatCount) {
|
||||
uint32_t deabstractDelta = TypeUtils::deabstractDeltaOfSize(registerSize());
|
||||
TypeId finalTypeId = TypeUtils::deabstract(typeId, deabstractDelta);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!TypeUtils::isValid(finalTypeId)))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
if (itemCount == 0 || repeatCount == 0)
|
||||
return kErrorOk;
|
||||
|
||||
uint32_t typeSize = TypeUtils::sizeOf(finalTypeId);
|
||||
Support::FastUInt8 of = 0;
|
||||
|
||||
size_t dataSize = Support::mulOverflow(itemCount, size_t(typeSize), &of);
|
||||
size_t totalSize = Support::mulOverflow(dataSize, repeatCount, &of);
|
||||
|
||||
if (ASMJIT_UNLIKELY(of))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
CodeWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, totalSize));
|
||||
|
||||
for (size_t i = 0; i < repeatCount; i++)
|
||||
writer.emitData(data, dataSize);
|
||||
|
||||
writer.done(this);
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger) {
|
||||
StringTmp<512> sb;
|
||||
Formatter::formatData(sb, _logger->flags(), arch(), typeId, data, itemCount, repeatCount);
|
||||
sb.append('\n');
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
static const TypeId dataTypeIdBySize[9] = {
|
||||
TypeId::kVoid, // [0] (invalid)
|
||||
TypeId::kUInt8, // [1] (uint8_t)
|
||||
TypeId::kUInt16, // [2] (uint16_t)
|
||||
TypeId::kVoid, // [3] (invalid)
|
||||
TypeId::kUInt32, // [4] (uint32_t)
|
||||
TypeId::kVoid, // [5] (invalid)
|
||||
TypeId::kVoid, // [6] (invalid)
|
||||
TypeId::kVoid, // [7] (invalid)
|
||||
TypeId::kUInt64 // [8] (uint64_t)
|
||||
};
|
||||
#endif
|
||||
|
||||
Error BaseAssembler::embedConstPool(const Label& label, const ConstPool& pool) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
if (ASMJIT_UNLIKELY(!isLabelValid(label)))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidLabel));
|
||||
|
||||
ASMJIT_PROPAGATE(align(AlignMode::kData, uint32_t(pool.alignment())));
|
||||
ASMJIT_PROPAGATE(bind(label));
|
||||
|
||||
size_t size = pool.size();
|
||||
if (!size)
|
||||
return kErrorOk;
|
||||
|
||||
CodeWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, size));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
uint8_t* data = writer.cursor();
|
||||
#endif
|
||||
|
||||
pool.fill(writer.cursor());
|
||||
writer.advance(size);
|
||||
writer.done(this);
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger) {
|
||||
uint32_t dataSizeLog2 = Support::min<uint32_t>(Support::ctz(pool.minItemSize()), 3);
|
||||
uint32_t dataSize = 1 << dataSizeLog2;
|
||||
|
||||
StringTmp<512> sb;
|
||||
Formatter::formatData(sb, _logger->flags(), arch(), dataTypeIdBySize[dataSize], data, size >> dataSizeLog2);
|
||||
sb.append('\n');
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedLabel(const Label& label, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
ASMJIT_ASSERT(_code != nullptr);
|
||||
RelocEntry* re;
|
||||
LabelEntry* le = _code->labelEntry(label);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!le))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidLabel));
|
||||
|
||||
if (dataSize == 0)
|
||||
dataSize = registerSize();
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidOperandSize));
|
||||
|
||||
CodeWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger) {
|
||||
StringTmp<256> sb;
|
||||
sb.append('.');
|
||||
Formatter::formatDataType(sb, _logger->flags(), arch(), dataTypeIdBySize[dataSize]);
|
||||
sb.append(' ');
|
||||
Formatter::formatLabel(sb, FormatFlags::kNone, this, label.id());
|
||||
sb.append('\n');
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
Error err = _code->newRelocEntry(&re, RelocType::kRelToAbs);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
re->_sourceSectionId = _section->id();
|
||||
re->_sourceOffset = offset();
|
||||
re->_format.resetToSimpleValue(OffsetType::kUnsignedOffset, dataSize);
|
||||
|
||||
if (le->isBound()) {
|
||||
re->_targetSectionId = le->section()->id();
|
||||
re->_payload = le->offset();
|
||||
}
|
||||
else {
|
||||
OffsetFormat of;
|
||||
of.resetToSimpleValue(OffsetType::kUnsignedOffset, dataSize);
|
||||
|
||||
LabelLink* link = _code->newLabelLink(le, _section->id(), offset(), 0, of);
|
||||
if (ASMJIT_UNLIKELY(!link))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
link->relocId = re->id();
|
||||
}
|
||||
|
||||
// Emit dummy DWORD/QWORD depending on the data size.
|
||||
writer.emitZeros(dataSize);
|
||||
writer.done(this);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
LabelEntry* labelEntry = _code->labelEntry(label);
|
||||
LabelEntry* baseEntry = _code->labelEntry(base);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!labelEntry || !baseEntry))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidLabel));
|
||||
|
||||
if (dataSize == 0)
|
||||
dataSize = registerSize();
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidOperandSize));
|
||||
|
||||
CodeWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger) {
|
||||
StringTmp<256> sb;
|
||||
sb.append('.');
|
||||
Formatter::formatDataType(sb, _logger->flags(), arch(), dataTypeIdBySize[dataSize]);
|
||||
sb.append(" (");
|
||||
Formatter::formatLabel(sb, FormatFlags::kNone, this, label.id());
|
||||
sb.append(" - ");
|
||||
Formatter::formatLabel(sb, FormatFlags::kNone, this, base.id());
|
||||
sb.append(")\n");
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If both labels are bound within the same section it means the delta can be calculated now.
|
||||
if (labelEntry->isBound() && baseEntry->isBound() && labelEntry->section() == baseEntry->section()) {
|
||||
uint64_t delta = labelEntry->offset() - baseEntry->offset();
|
||||
writer.emitValueLE(delta, dataSize);
|
||||
}
|
||||
else {
|
||||
RelocEntry* re;
|
||||
Error err = _code->newRelocEntry(&re, RelocType::kExpression);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
Expression* exp = _code->_zone.newT<Expression>();
|
||||
if (ASMJIT_UNLIKELY(!exp))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
exp->reset();
|
||||
exp->opType = ExpressionOpType::kSub;
|
||||
exp->setValueAsLabel(0, labelEntry);
|
||||
exp->setValueAsLabel(1, baseEntry);
|
||||
|
||||
re->_format.resetToSimpleValue(OffsetType::kSignedOffset, dataSize);
|
||||
re->_sourceSectionId = _section->id();
|
||||
re->_sourceOffset = offset();
|
||||
re->_payload = (uint64_t)(uintptr_t)exp;
|
||||
|
||||
writer.emitZeros(dataSize);
|
||||
}
|
||||
|
||||
writer.done(this);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseAssembler - Comment
|
||||
// =======================
|
||||
|
||||
Error BaseAssembler::comment(const char* data, size_t size) {
|
||||
if (!hasEmitterFlag(EmitterFlags::kLogComments)) {
|
||||
if (!hasEmitterFlag(EmitterFlags::kAttached))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
// Logger cannot be NULL if `EmitterFlags::kLogComments` is set.
|
||||
ASMJIT_ASSERT(_logger != nullptr);
|
||||
|
||||
_logger->log(data, size);
|
||||
_logger->log("\n", 1);
|
||||
return kErrorOk;
|
||||
#else
|
||||
DebugUtils::unused(data, size);
|
||||
return kErrorOk;
|
||||
#endif
|
||||
}
|
||||
|
||||
// BaseAssembler - Events
|
||||
// ======================
|
||||
|
||||
Error BaseAssembler::onAttach(CodeHolder* code) noexcept {
|
||||
ASMJIT_PROPAGATE(Base::onAttach(code));
|
||||
|
||||
// Attach to the end of the .text section.
|
||||
BaseAssembler_initSection(this, code->_sections[0]);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::onDetach(CodeHolder* code) noexcept {
|
||||
_section = nullptr;
|
||||
_bufferData = nullptr;
|
||||
_bufferEnd = nullptr;
|
||||
_bufferPtr = nullptr;
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
129
lib/lepton/asmjit/core/assembler.h
Normal file
129
lib/lepton/asmjit/core/assembler.h
Normal file
@ -0,0 +1,129 @@
|
||||
// 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_ASSEMBLER_H_INCLUDED
|
||||
#define ASMJIT_CORE_ASSEMBLER_H_INCLUDED
|
||||
|
||||
#include "../core/codeholder.h"
|
||||
#include "../core/emitter.h"
|
||||
#include "../core/operand.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_assembler
|
||||
//! \{
|
||||
|
||||
//! Base assembler.
|
||||
//!
|
||||
//! This is a base class that provides interface used by architecture specific
|
||||
//! assembler implementations. Assembler doesn't hold any data, instead it's
|
||||
//! attached to \ref CodeHolder, which provides all the data that Assembler
|
||||
//! needs and which can be altered by it.
|
||||
//!
|
||||
//! Check out architecture specific assemblers for more details and examples:
|
||||
//!
|
||||
//! - \ref x86::Assembler - X86/X64 assembler implementation.
|
||||
class ASMJIT_VIRTAPI BaseAssembler : public BaseEmitter {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(BaseAssembler)
|
||||
typedef BaseEmitter Base;
|
||||
|
||||
//! Current section where the assembling happens.
|
||||
Section* _section = nullptr;
|
||||
//! Start of the CodeBuffer of the current section.
|
||||
uint8_t* _bufferData = nullptr;
|
||||
//! End (first invalid byte) of the current section.
|
||||
uint8_t* _bufferEnd = nullptr;
|
||||
//! Pointer in the CodeBuffer of the current section.
|
||||
uint8_t* _bufferPtr = nullptr;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `BaseAssembler` instance.
|
||||
ASMJIT_API BaseAssembler() noexcept;
|
||||
//! Destroys the `BaseAssembler` instance.
|
||||
ASMJIT_API virtual ~BaseAssembler() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Code-Buffer Management
|
||||
//! \{
|
||||
|
||||
//! Returns the capacity of the current CodeBuffer.
|
||||
inline size_t bufferCapacity() const noexcept { return (size_t)(_bufferEnd - _bufferData); }
|
||||
//! Returns the number of remaining bytes in the current CodeBuffer.
|
||||
inline size_t remainingSpace() const noexcept { return (size_t)(_bufferEnd - _bufferPtr); }
|
||||
|
||||
//! Returns the current position in the CodeBuffer.
|
||||
inline size_t offset() const noexcept { return (size_t)(_bufferPtr - _bufferData); }
|
||||
|
||||
//! Sets the current position in the CodeBuffer to `offset`.
|
||||
//!
|
||||
//! \note The `offset` cannot be greater than buffer size even if it's
|
||||
//! within the buffer's capacity.
|
||||
ASMJIT_API Error setOffset(size_t offset);
|
||||
|
||||
//! Returns the start of the CodeBuffer in the current section.
|
||||
inline uint8_t* bufferData() const noexcept { return _bufferData; }
|
||||
//! Returns the end (first invalid byte) in the current section.
|
||||
inline uint8_t* bufferEnd() const noexcept { return _bufferEnd; }
|
||||
//! Returns the current pointer in the CodeBuffer in the current section.
|
||||
inline uint8_t* bufferPtr() const noexcept { return _bufferPtr; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Section Management
|
||||
//! \{
|
||||
|
||||
//! Returns the current section.
|
||||
inline Section* currentSection() const noexcept { return _section; }
|
||||
|
||||
ASMJIT_API Error section(Section* section) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Label Management
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Label newLabel() override;
|
||||
ASMJIT_API Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, LabelType type = LabelType::kGlobal, uint32_t parentId = Globals::kInvalidId) override;
|
||||
ASMJIT_API Error bind(const Label& label) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Embed
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error embed(const void* data, size_t dataSize) override;
|
||||
ASMJIT_API Error embedDataArray(TypeId typeId, const void* data, size_t itemCount, size_t repeatCount = 1) override;
|
||||
ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override;
|
||||
|
||||
ASMJIT_API Error embedLabel(const Label& label, size_t dataSize = 0) override;
|
||||
ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, size_t dataSize = 0) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Comment
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error comment(const char* data, size_t size = SIZE_MAX) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
|
||||
ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_ASSEMBLER_H_INCLUDED
|
||||
889
lib/lepton/asmjit/core/builder.cpp
Normal file
889
lib/lepton/asmjit/core/builder.cpp
Normal file
@ -0,0 +1,889 @@
|
||||
// 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"
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
|
||||
#include "../core/builder.h"
|
||||
#include "../core/emitterutils_p.h"
|
||||
#include "../core/errorhandler.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// PostponedErrorHandler (Internal)
|
||||
// ================================
|
||||
|
||||
//! Postponed error handler that never throws. Used as a temporal error handler
|
||||
//! to run passes. If error occurs, the caller is notified and will call the
|
||||
//! real error handler, that can throw.
|
||||
class PostponedErrorHandler : public ErrorHandler {
|
||||
public:
|
||||
void handleError(Error err, const char* message, BaseEmitter* origin) override {
|
||||
DebugUtils::unused(err, origin);
|
||||
_message.assign(message);
|
||||
}
|
||||
|
||||
StringTmp<128> _message;
|
||||
};
|
||||
|
||||
// BaseBuilder - Utilities
|
||||
// =======================
|
||||
|
||||
static void BaseBuilder_deletePasses(BaseBuilder* self) noexcept {
|
||||
for (Pass* pass : self->_passes)
|
||||
pass->~Pass();
|
||||
self->_passes.reset();
|
||||
}
|
||||
|
||||
// BaseBuilder - Construction & Destruction
|
||||
// ========================================
|
||||
|
||||
BaseBuilder::BaseBuilder() noexcept
|
||||
: BaseEmitter(EmitterType::kBuilder),
|
||||
_codeZone(32768 - Zone::kBlockOverhead),
|
||||
_dataZone(16384 - Zone::kBlockOverhead),
|
||||
_passZone(65536 - Zone::kBlockOverhead),
|
||||
_allocator(&_codeZone) {}
|
||||
|
||||
BaseBuilder::~BaseBuilder() noexcept {
|
||||
BaseBuilder_deletePasses(this);
|
||||
}
|
||||
|
||||
// BaseBuilder - Node Management
|
||||
// =============================
|
||||
|
||||
Error BaseBuilder::newInstNode(InstNode** out, InstId instId, InstOptions instOptions, uint32_t opCount) {
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= InstNode::kBaseOpCapacity);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
*out = new(node) InstNode(this, instId, instOptions, opCount, opCapacity);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
|
||||
Error BaseBuilder::newLabelNode(LabelNode** out) {
|
||||
*out = nullptr;
|
||||
|
||||
ASMJIT_PROPAGATE(_newNodeT<LabelNode>(out));
|
||||
return registerLabelNode(*out);
|
||||
}
|
||||
|
||||
Error BaseBuilder::newAlignNode(AlignNode** out, AlignMode alignMode, uint32_t alignment) {
|
||||
*out = nullptr;
|
||||
return _newNodeT<AlignNode>(out, alignMode, alignment);
|
||||
}
|
||||
|
||||
Error BaseBuilder::newEmbedDataNode(EmbedDataNode** out, TypeId typeId, const void* data, size_t itemCount, size_t repeatCount) {
|
||||
*out = nullptr;
|
||||
|
||||
uint32_t deabstractDelta = TypeUtils::deabstractDeltaOfSize(registerSize());
|
||||
TypeId finalTypeId = TypeUtils::deabstract(typeId, deabstractDelta);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!TypeUtils::isValid(finalTypeId)))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
uint32_t typeSize = TypeUtils::sizeOf(finalTypeId);
|
||||
Support::FastUInt8 of = 0;
|
||||
|
||||
size_t dataSize = Support::mulOverflow(itemCount, size_t(typeSize), &of);
|
||||
if (ASMJIT_UNLIKELY(of))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<EmbedDataNode>(&node));
|
||||
|
||||
node->_embed._typeId = typeId;
|
||||
node->_embed._typeSize = uint8_t(typeSize);
|
||||
node->_itemCount = itemCount;
|
||||
node->_repeatCount = repeatCount;
|
||||
|
||||
uint8_t* dstData = node->_inlineData;
|
||||
if (dataSize > EmbedDataNode::kInlineBufferSize) {
|
||||
dstData = static_cast<uint8_t*>(_dataZone.alloc(dataSize, 8));
|
||||
if (ASMJIT_UNLIKELY(!dstData))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
node->_externalData = dstData;
|
||||
}
|
||||
|
||||
if (data)
|
||||
memcpy(dstData, data, dataSize);
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::newConstPoolNode(ConstPoolNode** out) {
|
||||
*out = nullptr;
|
||||
|
||||
ASMJIT_PROPAGATE(_newNodeT<ConstPoolNode>(out));
|
||||
return registerLabelNode(*out);
|
||||
}
|
||||
|
||||
Error BaseBuilder::newCommentNode(CommentNode** out, const char* data, size_t size) {
|
||||
*out = nullptr;
|
||||
|
||||
if (data) {
|
||||
if (size == SIZE_MAX)
|
||||
size = strlen(data);
|
||||
|
||||
if (size > 0) {
|
||||
data = static_cast<char*>(_dataZone.dup(data, size, true));
|
||||
if (ASMJIT_UNLIKELY(!data))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
}
|
||||
|
||||
return _newNodeT<CommentNode>(out, data);
|
||||
}
|
||||
|
||||
BaseNode* BaseBuilder::addNode(BaseNode* node) noexcept {
|
||||
ASMJIT_ASSERT(!node->_prev);
|
||||
ASMJIT_ASSERT(!node->_next);
|
||||
ASMJIT_ASSERT(!node->isActive());
|
||||
|
||||
if (!_cursor) {
|
||||
if (!_firstNode) {
|
||||
_firstNode = node;
|
||||
_lastNode = node;
|
||||
}
|
||||
else {
|
||||
node->_next = _firstNode;
|
||||
_firstNode->_prev = node;
|
||||
_firstNode = node;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BaseNode* prev = _cursor;
|
||||
BaseNode* next = _cursor->next();
|
||||
|
||||
node->_prev = prev;
|
||||
node->_next = next;
|
||||
|
||||
prev->_next = node;
|
||||
if (next)
|
||||
next->_prev = node;
|
||||
else
|
||||
_lastNode = node;
|
||||
}
|
||||
|
||||
node->addFlags(NodeFlags::kIsActive);
|
||||
if (node->isSection())
|
||||
_dirtySectionLinks = true;
|
||||
|
||||
_cursor = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
BaseNode* BaseBuilder::addAfter(BaseNode* node, BaseNode* ref) noexcept {
|
||||
ASMJIT_ASSERT(!node->_prev);
|
||||
ASMJIT_ASSERT(!node->_next);
|
||||
|
||||
BaseNode* prev = ref;
|
||||
BaseNode* next = ref->next();
|
||||
|
||||
node->_prev = prev;
|
||||
node->_next = next;
|
||||
|
||||
node->addFlags(NodeFlags::kIsActive);
|
||||
if (node->isSection())
|
||||
_dirtySectionLinks = true;
|
||||
|
||||
prev->_next = node;
|
||||
if (next)
|
||||
next->_prev = node;
|
||||
else
|
||||
_lastNode = node;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
BaseNode* BaseBuilder::addBefore(BaseNode* node, BaseNode* ref) noexcept {
|
||||
ASMJIT_ASSERT(!node->_prev);
|
||||
ASMJIT_ASSERT(!node->_next);
|
||||
ASMJIT_ASSERT(!node->isActive());
|
||||
ASMJIT_ASSERT(ref->isActive());
|
||||
|
||||
BaseNode* prev = ref->prev();
|
||||
BaseNode* next = ref;
|
||||
|
||||
node->_prev = prev;
|
||||
node->_next = next;
|
||||
|
||||
node->addFlags(NodeFlags::kIsActive);
|
||||
if (node->isSection())
|
||||
_dirtySectionLinks = true;
|
||||
|
||||
next->_prev = node;
|
||||
if (prev)
|
||||
prev->_next = node;
|
||||
else
|
||||
_firstNode = node;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
BaseNode* BaseBuilder::removeNode(BaseNode* node) noexcept {
|
||||
if (!node->isActive())
|
||||
return node;
|
||||
|
||||
BaseNode* prev = node->prev();
|
||||
BaseNode* next = node->next();
|
||||
|
||||
if (_firstNode == node)
|
||||
_firstNode = next;
|
||||
else
|
||||
prev->_next = next;
|
||||
|
||||
if (_lastNode == node)
|
||||
_lastNode = prev;
|
||||
else
|
||||
next->_prev = prev;
|
||||
|
||||
node->_prev = nullptr;
|
||||
node->_next = nullptr;
|
||||
node->clearFlags(NodeFlags::kIsActive);
|
||||
if (node->isSection())
|
||||
_dirtySectionLinks = true;
|
||||
|
||||
if (_cursor == node)
|
||||
_cursor = prev;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void BaseBuilder::removeNodes(BaseNode* first, BaseNode* last) noexcept {
|
||||
if (first == last) {
|
||||
removeNode(first);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!first->isActive())
|
||||
return;
|
||||
|
||||
BaseNode* prev = first->prev();
|
||||
BaseNode* next = last->next();
|
||||
|
||||
if (_firstNode == first)
|
||||
_firstNode = next;
|
||||
else
|
||||
prev->_next = next;
|
||||
|
||||
if (_lastNode == last)
|
||||
_lastNode = prev;
|
||||
else
|
||||
next->_prev = prev;
|
||||
|
||||
BaseNode* node = first;
|
||||
uint32_t didRemoveSection = false;
|
||||
|
||||
for (;;) {
|
||||
next = node->next();
|
||||
ASMJIT_ASSERT(next != nullptr);
|
||||
|
||||
node->_prev = nullptr;
|
||||
node->_next = nullptr;
|
||||
node->clearFlags(NodeFlags::kIsActive);
|
||||
didRemoveSection |= uint32_t(node->isSection());
|
||||
|
||||
if (_cursor == node)
|
||||
_cursor = prev;
|
||||
|
||||
if (node == last)
|
||||
break;
|
||||
node = next;
|
||||
}
|
||||
|
||||
if (didRemoveSection)
|
||||
_dirtySectionLinks = true;
|
||||
}
|
||||
|
||||
BaseNode* BaseBuilder::setCursor(BaseNode* node) noexcept {
|
||||
BaseNode* old = _cursor;
|
||||
_cursor = node;
|
||||
return old;
|
||||
}
|
||||
|
||||
// BaseBuilder - Sections
|
||||
// ======================
|
||||
|
||||
Error BaseBuilder::sectionNodeOf(SectionNode** out, uint32_t sectionId) {
|
||||
*out = nullptr;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!_code->isSectionValid(sectionId)))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidSection));
|
||||
|
||||
if (sectionId >= _sectionNodes.size()) {
|
||||
Error err = _sectionNodes.reserve(&_allocator, sectionId + 1);
|
||||
if (ASMJIT_UNLIKELY(err != kErrorOk))
|
||||
return reportError(err);
|
||||
}
|
||||
|
||||
SectionNode* node = nullptr;
|
||||
if (sectionId < _sectionNodes.size())
|
||||
node = _sectionNodes[sectionId];
|
||||
|
||||
if (!node) {
|
||||
ASMJIT_PROPAGATE(_newNodeT<SectionNode>(&node, sectionId));
|
||||
|
||||
// We have already reserved enough space, this cannot fail now.
|
||||
if (sectionId >= _sectionNodes.size())
|
||||
_sectionNodes.resize(&_allocator, sectionId + 1);
|
||||
|
||||
_sectionNodes[sectionId] = node;
|
||||
}
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::section(Section* section) {
|
||||
SectionNode* node;
|
||||
ASMJIT_PROPAGATE(sectionNodeOf(&node, section->id()));
|
||||
ASMJIT_ASSUME(node != nullptr);
|
||||
|
||||
if (!node->isActive()) {
|
||||
// Insert the section at the end if it was not part of the code.
|
||||
addAfter(node, lastNode());
|
||||
_cursor = node;
|
||||
}
|
||||
else {
|
||||
// This is a bit tricky. We cache section links to make sure that
|
||||
// switching sections doesn't involve traversal in linked-list unless
|
||||
// the position of the section has changed.
|
||||
if (hasDirtySectionLinks())
|
||||
updateSectionLinks();
|
||||
|
||||
if (node->_nextSection)
|
||||
_cursor = node->_nextSection->_prev;
|
||||
else
|
||||
_cursor = _lastNode;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
void BaseBuilder::updateSectionLinks() noexcept {
|
||||
if (!_dirtySectionLinks)
|
||||
return;
|
||||
|
||||
BaseNode* node_ = _firstNode;
|
||||
SectionNode* currentSection = nullptr;
|
||||
|
||||
while (node_) {
|
||||
if (node_->isSection()) {
|
||||
if (currentSection)
|
||||
currentSection->_nextSection = node_->as<SectionNode>();
|
||||
currentSection = node_->as<SectionNode>();
|
||||
}
|
||||
node_ = node_->next();
|
||||
}
|
||||
|
||||
if (currentSection)
|
||||
currentSection->_nextSection = nullptr;
|
||||
|
||||
_dirtySectionLinks = false;
|
||||
}
|
||||
|
||||
// BaseBuilder - Labels
|
||||
// ====================
|
||||
|
||||
Error BaseBuilder::labelNodeOf(LabelNode** out, uint32_t labelId) {
|
||||
*out = nullptr;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
uint32_t index = labelId;
|
||||
if (ASMJIT_UNLIKELY(index >= _code->labelCount()))
|
||||
return DebugUtils::errored(kErrorInvalidLabel);
|
||||
|
||||
if (index >= _labelNodes.size())
|
||||
ASMJIT_PROPAGATE(_labelNodes.resize(&_allocator, index + 1));
|
||||
|
||||
LabelNode* node = _labelNodes[index];
|
||||
if (!node) {
|
||||
ASMJIT_PROPAGATE(_newNodeT<LabelNode>(&node, labelId));
|
||||
_labelNodes[index] = node;
|
||||
}
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::registerLabelNode(LabelNode* node) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
LabelEntry* le;
|
||||
ASMJIT_PROPAGATE(_code->newLabelEntry(&le));
|
||||
uint32_t labelId = le->id();
|
||||
|
||||
// We just added one label so it must be true.
|
||||
ASMJIT_ASSERT(_labelNodes.size() < labelId + 1);
|
||||
ASMJIT_PROPAGATE(_labelNodes.resize(&_allocator, labelId + 1));
|
||||
|
||||
_labelNodes[labelId] = node;
|
||||
node->_labelId = labelId;
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
static Error BaseBuilder_newLabelInternal(BaseBuilder* self, uint32_t labelId) {
|
||||
ASMJIT_ASSERT(self->_labelNodes.size() < labelId + 1);
|
||||
|
||||
uint32_t growBy = labelId - self->_labelNodes.size();
|
||||
Error err = self->_labelNodes.willGrow(&self->_allocator, growBy);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return self->reportError(err);
|
||||
|
||||
LabelNode* node;
|
||||
ASMJIT_PROPAGATE(self->_newNodeT<LabelNode>(&node, labelId));
|
||||
|
||||
self->_labelNodes.resize(&self->_allocator, labelId + 1);
|
||||
self->_labelNodes[labelId] = node;
|
||||
node->_labelId = labelId;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Label BaseBuilder::newLabel() {
|
||||
uint32_t labelId = Globals::kInvalidId;
|
||||
LabelEntry* le;
|
||||
|
||||
if (_code &&
|
||||
_code->newLabelEntry(&le) == kErrorOk &&
|
||||
BaseBuilder_newLabelInternal(this, le->id()) == kErrorOk) {
|
||||
labelId = le->id();
|
||||
}
|
||||
|
||||
return Label(labelId);
|
||||
}
|
||||
|
||||
Label BaseBuilder::newNamedLabel(const char* name, size_t nameSize, LabelType type, uint32_t parentId) {
|
||||
uint32_t labelId = Globals::kInvalidId;
|
||||
LabelEntry* le;
|
||||
|
||||
if (_code &&
|
||||
_code->newNamedLabelEntry(&le, name, nameSize, type, parentId) == kErrorOk &&
|
||||
BaseBuilder_newLabelInternal(this, le->id()) == kErrorOk) {
|
||||
labelId = le->id();
|
||||
}
|
||||
|
||||
return Label(labelId);
|
||||
}
|
||||
|
||||
Error BaseBuilder::bind(const Label& label) {
|
||||
LabelNode* node;
|
||||
ASMJIT_PROPAGATE(labelNodeOf(&node, label));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseBuilder - Passes
|
||||
// ====================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Pass* BaseBuilder::passByName(const char* name) const noexcept {
|
||||
for (Pass* pass : _passes)
|
||||
if (strcmp(pass->name(), name) == 0)
|
||||
return pass;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error BaseBuilder::addPass(Pass* pass) noexcept {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (ASMJIT_UNLIKELY(pass == nullptr)) {
|
||||
// Since this is directly called by `addPassT()` we treat `null` argument
|
||||
// as out-of-memory condition. Otherwise it would be API misuse.
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
}
|
||||
else if (ASMJIT_UNLIKELY(pass->_cb)) {
|
||||
// Kinda weird, but okay...
|
||||
if (pass->_cb == this)
|
||||
return kErrorOk;
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(_passes.append(&_allocator, pass));
|
||||
pass->_cb = this;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error BaseBuilder::deletePass(Pass* pass) noexcept {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (ASMJIT_UNLIKELY(pass == nullptr))
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
|
||||
if (pass->_cb != nullptr) {
|
||||
if (pass->_cb != this)
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
uint32_t index = _passes.indexOf(pass);
|
||||
ASMJIT_ASSERT(index != Globals::kNotFound);
|
||||
|
||||
pass->_cb = nullptr;
|
||||
_passes.removeAt(index);
|
||||
}
|
||||
|
||||
pass->~Pass();
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::runPasses() {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (_passes.empty())
|
||||
return kErrorOk;
|
||||
|
||||
ErrorHandler* prev = errorHandler();
|
||||
PostponedErrorHandler postponed;
|
||||
|
||||
Error err = kErrorOk;
|
||||
setErrorHandler(&postponed);
|
||||
|
||||
for (Pass* pass : _passes) {
|
||||
_passZone.reset();
|
||||
err = pass->run(&_passZone, _logger);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
_passZone.reset();
|
||||
setErrorHandler(prev);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err, !postponed._message.empty() ? postponed._message.data() : nullptr);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseBuilder - Emit
|
||||
// ==================
|
||||
|
||||
Error BaseBuilder::_emit(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) {
|
||||
uint32_t opCount = EmitterUtils::opCountFromEmitArgs(o0, o1, o2, opExt);
|
||||
InstOptions options = instOptions() | forcedInstOptions();
|
||||
|
||||
if (Support::test(options, InstOptions::kReserved)) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
// Strict validation.
|
||||
if (hasDiagnosticOption(DiagnosticOptions::kValidateIntermediate)) {
|
||||
Operand_ opArray[Globals::kMaxOpCount];
|
||||
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
|
||||
|
||||
ValidationFlags validationFlags = isCompiler() ? ValidationFlags::kEnableVirtRegs : ValidationFlags::kNone;
|
||||
Error err = _funcs.validate(arch(), BaseInst(instId, options, _extraReg), opArray, opCount, validationFlags);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
resetInstOptions();
|
||||
resetExtraReg();
|
||||
resetInlineComment();
|
||||
return reportError(err);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Clear instruction options that should never be part of a regular instruction.
|
||||
options &= ~InstOptions::kReserved;
|
||||
}
|
||||
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= InstNode::kBaseOpCapacity);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
const char* comment = inlineComment();
|
||||
|
||||
resetInstOptions();
|
||||
resetInlineComment();
|
||||
|
||||
if (ASMJIT_UNLIKELY(!node)) {
|
||||
resetExtraReg();
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
|
||||
node = new(node) InstNode(this, instId, options, opCount, opCapacity);
|
||||
node->setExtraReg(extraReg());
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->setOp(2, o2);
|
||||
for (uint32_t i = 3; i < opCount; i++)
|
||||
node->setOp(i, opExt[i - 3]);
|
||||
node->resetOpRange(opCount, opCapacity);
|
||||
|
||||
if (comment)
|
||||
node->setInlineComment(static_cast<char*>(_dataZone.dup(comment, strlen(comment), true)));
|
||||
|
||||
addNode(node);
|
||||
resetExtraReg();
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseBuilder - Align
|
||||
// ===================
|
||||
|
||||
Error BaseBuilder::align(AlignMode alignMode, uint32_t alignment) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
AlignNode* node;
|
||||
ASMJIT_PROPAGATE(newAlignNode(&node, alignMode, alignment));
|
||||
ASMJIT_ASSUME(node != nullptr);
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseBuilder - Embed
|
||||
// ===================
|
||||
|
||||
Error BaseBuilder::embed(const void* data, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(newEmbedDataNode(&node, TypeId::kUInt8, data, dataSize));
|
||||
ASMJIT_ASSUME(node != nullptr);
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedDataArray(TypeId typeId, const void* data, size_t itemCount, size_t itemRepeat) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(newEmbedDataNode(&node, typeId, data, itemCount, itemRepeat));
|
||||
ASMJIT_ASSUME(node != nullptr);
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedConstPool(const Label& label, const ConstPool& pool) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (!isLabelValid(label))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidLabel));
|
||||
|
||||
ASMJIT_PROPAGATE(align(AlignMode::kData, uint32_t(pool.alignment())));
|
||||
ASMJIT_PROPAGATE(bind(label));
|
||||
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(newEmbedDataNode(&node, TypeId::kUInt8, nullptr, pool.size()));
|
||||
ASMJIT_ASSUME(node != nullptr);
|
||||
|
||||
pool.fill(node->data());
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseBuilder - EmbedLabel & EmbedLabelDelta
|
||||
// ==========================================
|
||||
//
|
||||
// If dataSize is zero it means that the size is the same as target register width, however,
|
||||
// if it's provided we really want to validate whether it's within the possible range.
|
||||
|
||||
static inline bool BaseBuilder_checkDataSize(size_t dataSize) noexcept {
|
||||
return !dataSize || (Support::isPowerOf2(dataSize) && dataSize <= 8);
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedLabel(const Label& label, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (!BaseBuilder_checkDataSize(dataSize))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
EmbedLabelNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<EmbedLabelNode>(&node, label.id(), uint32_t(dataSize)));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedLabelDelta(const Label& label, const Label& base, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (!BaseBuilder_checkDataSize(dataSize))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
EmbedLabelDeltaNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<EmbedLabelDeltaNode>(&node, label.id(), base.id(), uint32_t(dataSize)));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseBuilder - Comment
|
||||
// =====================
|
||||
|
||||
Error BaseBuilder::comment(const char* data, size_t size) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
CommentNode* node;
|
||||
ASMJIT_PROPAGATE(newCommentNode(&node, data, size));
|
||||
ASMJIT_ASSUME(node != nullptr);
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseBuilder - SerializeTo
|
||||
// =========================
|
||||
|
||||
Error BaseBuilder::serializeTo(BaseEmitter* dst) {
|
||||
Error err = kErrorOk;
|
||||
BaseNode* node_ = _firstNode;
|
||||
|
||||
Operand_ opArray[Globals::kMaxOpCount];
|
||||
|
||||
do {
|
||||
dst->setInlineComment(node_->inlineComment());
|
||||
|
||||
if (node_->isInst()) {
|
||||
InstNode* node = node_->as<InstNode>();
|
||||
|
||||
// NOTE: Inlined to remove one additional call per instruction.
|
||||
dst->setInstOptions(node->options());
|
||||
dst->setExtraReg(node->extraReg());
|
||||
|
||||
const Operand_* op = node->operands();
|
||||
const Operand_* opExt = EmitterUtils::noExt;
|
||||
|
||||
uint32_t opCount = node->opCount();
|
||||
if (opCount > 3) {
|
||||
uint32_t i = 4;
|
||||
opArray[3] = op[3];
|
||||
|
||||
while (i < opCount) {
|
||||
opArray[i].copyFrom(op[i]);
|
||||
i++;
|
||||
}
|
||||
while (i < Globals::kMaxOpCount) {
|
||||
opArray[i].reset();
|
||||
i++;
|
||||
}
|
||||
opExt = opArray + 3;
|
||||
}
|
||||
|
||||
err = dst->_emit(node->id(), op[0], op[1], op[2], opExt);
|
||||
}
|
||||
else if (node_->isLabel()) {
|
||||
if (node_->isConstPool()) {
|
||||
ConstPoolNode* node = node_->as<ConstPoolNode>();
|
||||
err = dst->embedConstPool(node->label(), node->constPool());
|
||||
}
|
||||
else {
|
||||
LabelNode* node = node_->as<LabelNode>();
|
||||
err = dst->bind(node->label());
|
||||
}
|
||||
}
|
||||
else if (node_->isAlign()) {
|
||||
AlignNode* node = node_->as<AlignNode>();
|
||||
err = dst->align(node->alignMode(), node->alignment());
|
||||
}
|
||||
else if (node_->isEmbedData()) {
|
||||
EmbedDataNode* node = node_->as<EmbedDataNode>();
|
||||
err = dst->embedDataArray(node->typeId(), node->data(), node->itemCount(), node->repeatCount());
|
||||
}
|
||||
else if (node_->isEmbedLabel()) {
|
||||
EmbedLabelNode* node = node_->as<EmbedLabelNode>();
|
||||
err = dst->embedLabel(node->label(), node->dataSize());
|
||||
}
|
||||
else if (node_->isEmbedLabelDelta()) {
|
||||
EmbedLabelDeltaNode* node = node_->as<EmbedLabelDeltaNode>();
|
||||
err = dst->embedLabelDelta(node->label(), node->baseLabel(), node->dataSize());
|
||||
}
|
||||
else if (node_->isSection()) {
|
||||
SectionNode* node = node_->as<SectionNode>();
|
||||
err = dst->section(_code->sectionById(node->id()));
|
||||
}
|
||||
else if (node_->isComment()) {
|
||||
CommentNode* node = node_->as<CommentNode>();
|
||||
err = dst->comment(node->inlineComment());
|
||||
}
|
||||
|
||||
if (err) break;
|
||||
node_ = node_->next();
|
||||
} while (node_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// BaseBuilder - Events
|
||||
// ====================
|
||||
|
||||
Error BaseBuilder::onAttach(CodeHolder* code) noexcept {
|
||||
ASMJIT_PROPAGATE(Base::onAttach(code));
|
||||
|
||||
SectionNode* initialSection;
|
||||
Error err = sectionNodeOf(&initialSection, 0);
|
||||
|
||||
if (!err)
|
||||
err = _passes.willGrow(&_allocator, 8);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
onDetach(code);
|
||||
return err;
|
||||
}
|
||||
|
||||
ASMJIT_ASSUME(initialSection != nullptr);
|
||||
_cursor = initialSection;
|
||||
_firstNode = initialSection;
|
||||
_lastNode = initialSection;
|
||||
initialSection->setFlags(NodeFlags::kIsActive);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::onDetach(CodeHolder* code) noexcept {
|
||||
BaseBuilder_deletePasses(this);
|
||||
_sectionNodes.reset();
|
||||
_labelNodes.reset();
|
||||
|
||||
_allocator.reset(&_codeZone);
|
||||
_codeZone.reset();
|
||||
_dataZone.reset();
|
||||
_passZone.reset();
|
||||
|
||||
_nodeFlags = NodeFlags::kNone;
|
||||
_cursor = nullptr;
|
||||
_firstNode = nullptr;
|
||||
_lastNode = nullptr;
|
||||
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
// Pass - Construction & Destruction
|
||||
// =================================
|
||||
|
||||
Pass::Pass(const char* name) noexcept
|
||||
: _name(name) {}
|
||||
Pass::~Pass() noexcept {}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_BUILDER
|
||||
1391
lib/lepton/asmjit/core/builder.h
Normal file
1391
lib/lepton/asmjit/core/builder.h
Normal file
File diff suppressed because it is too large
Load Diff
113
lib/lepton/asmjit/core/codebuffer.h
Normal file
113
lib/lepton/asmjit/core/codebuffer.h
Normal file
@ -0,0 +1,113 @@
|
||||
// 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_CODEBUFFER_H_INCLUDED
|
||||
#define ASMJIT_CORE_CODEBUFFER_H_INCLUDED
|
||||
|
||||
#include "../core/globals.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
//! Flags used by \ref CodeBuffer.
|
||||
enum class CodeBufferFlags : uint32_t {
|
||||
//! No flags.
|
||||
kNone = 0,
|
||||
//! Buffer is external (not allocated by asmjit).
|
||||
kIsExternal = 0x00000001u,
|
||||
//! Buffer is fixed (cannot be reallocated).
|
||||
kIsFixed = 0x00000002u
|
||||
};
|
||||
ASMJIT_DEFINE_ENUM_FLAGS(CodeBufferFlags)
|
||||
|
||||
//! Code or data buffer.
|
||||
struct CodeBuffer {
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! The content of the buffer (data).
|
||||
uint8_t* _data;
|
||||
//! Number of bytes of `data` used.
|
||||
size_t _size;
|
||||
//! Buffer capacity (in bytes).
|
||||
size_t _capacity;
|
||||
//! Buffer flags.
|
||||
CodeBufferFlags _flags;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
//! Returns a referebce to the byte at the given `index`.
|
||||
inline uint8_t& operator[](size_t index) noexcept {
|
||||
ASMJIT_ASSERT(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
//! \overload
|
||||
inline const uint8_t& operator[](size_t index) const noexcept {
|
||||
ASMJIT_ASSERT(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns code buffer flags.
|
||||
inline CodeBufferFlags flags() const noexcept { return _flags; }
|
||||
//! Tests whether the code buffer has the given `flag` set.
|
||||
inline bool hasFlag(CodeBufferFlags flag) const noexcept { return Support::test(_flags, flag); }
|
||||
|
||||
//! Tests whether this code buffer has a fixed size.
|
||||
//!
|
||||
//! Fixed size means that the code buffer is fixed and cannot grow.
|
||||
inline bool isFixed() const noexcept { return hasFlag(CodeBufferFlags::kIsFixed); }
|
||||
|
||||
//! Tests whether the data in this code buffer is external.
|
||||
//!
|
||||
//! External data can only be provided by users, it's never used by AsmJit.
|
||||
inline bool isExternal() const noexcept { return hasFlag(CodeBufferFlags::kIsExternal); }
|
||||
|
||||
//! Tests whether the data in this code buffer is allocated (non-null).
|
||||
inline bool isAllocated() const noexcept { return _data != nullptr; }
|
||||
|
||||
//! Tests whether the code buffer is empty.
|
||||
inline bool empty() const noexcept { return !_size; }
|
||||
|
||||
//! Returns the size of the data.
|
||||
inline size_t size() const noexcept { return _size; }
|
||||
//! Returns the capacity of the data.
|
||||
inline size_t capacity() const noexcept { return _capacity; }
|
||||
|
||||
//! Returns the pointer to the data the buffer references.
|
||||
inline uint8_t* data() noexcept { return _data; }
|
||||
//! \overload
|
||||
inline const uint8_t* data() const noexcept { return _data; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Iterators
|
||||
//! \{
|
||||
|
||||
inline uint8_t* begin() noexcept { return _data; }
|
||||
inline const uint8_t* begin() const noexcept { return _data; }
|
||||
|
||||
inline uint8_t* end() noexcept { return _data + _size; }
|
||||
inline const uint8_t* end() const noexcept { return _data + _size; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_CODEBUFFER_H_INCLUDED
|
||||
|
||||
1149
lib/lepton/asmjit/core/codeholder.cpp
Normal file
1149
lib/lepton/asmjit/core/codeholder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1035
lib/lepton/asmjit/core/codeholder.h
Normal file
1035
lib/lepton/asmjit/core/codeholder.h
Normal file
File diff suppressed because it is too large
Load Diff
175
lib/lepton/asmjit/core/codewriter.cpp
Normal file
175
lib/lepton/asmjit/core/codewriter.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
// 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/codeholder.h"
|
||||
#include "../core/codewriter_p.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
bool CodeWriterUtils::encodeOffset32(uint32_t* dst, int64_t offset64, const OffsetFormat& format) noexcept {
|
||||
uint32_t bitCount = format.immBitCount();
|
||||
uint32_t bitShift = format.immBitShift();
|
||||
uint32_t discardLsb = format.immDiscardLsb();
|
||||
|
||||
// Invalid offset (should not happen).
|
||||
if (!bitCount || bitCount > format.valueSize() * 8u)
|
||||
return false;
|
||||
|
||||
uint32_t value;
|
||||
|
||||
// First handle all unsigned offset types.
|
||||
if (format.type() == OffsetType::kUnsignedOffset) {
|
||||
if (discardLsb) {
|
||||
ASMJIT_ASSERT(discardLsb <= 32);
|
||||
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
|
||||
return false;
|
||||
offset64 = int64_t(uint64_t(offset64) >> discardLsb);
|
||||
}
|
||||
|
||||
value = uint32_t(offset64 & Support::lsbMask<uint32_t>(bitCount));
|
||||
if (value != offset64)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// The rest of OffsetType options are all signed.
|
||||
if (discardLsb) {
|
||||
ASMJIT_ASSERT(discardLsb <= 32);
|
||||
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
|
||||
return false;
|
||||
offset64 >>= discardLsb;
|
||||
}
|
||||
|
||||
if (!Support::isInt32(offset64))
|
||||
return false;
|
||||
|
||||
value = uint32_t(int32_t(offset64));
|
||||
if (!Support::isEncodableOffset32(int32_t(value), bitCount))
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (format.type()) {
|
||||
case OffsetType::kSignedOffset:
|
||||
case OffsetType::kUnsignedOffset: {
|
||||
*dst = (value & Support::lsbMask<uint32_t>(bitCount)) << bitShift;
|
||||
return true;
|
||||
}
|
||||
|
||||
case OffsetType::kAArch64_ADR:
|
||||
case OffsetType::kAArch64_ADRP: {
|
||||
// Sanity checks.
|
||||
if (format.valueSize() != 4 || bitCount != 21 || bitShift != 5)
|
||||
return false;
|
||||
|
||||
uint32_t immLo = value & 0x3u;
|
||||
uint32_t immHi = (value >> 2) & Support::lsbMask<uint32_t>(19);
|
||||
|
||||
*dst = (immLo << 29) | (immHi << 5);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CodeWriterUtils::encodeOffset64(uint64_t* dst, int64_t offset64, const OffsetFormat& format) noexcept {
|
||||
uint32_t bitCount = format.immBitCount();
|
||||
uint32_t discardLsb = format.immDiscardLsb();
|
||||
|
||||
if (!bitCount || bitCount > format.valueSize() * 8u)
|
||||
return false;
|
||||
|
||||
uint64_t value;
|
||||
|
||||
// First handle all unsigned offset types.
|
||||
if (format.type() == OffsetType::kUnsignedOffset) {
|
||||
if (discardLsb) {
|
||||
ASMJIT_ASSERT(discardLsb <= 32);
|
||||
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
|
||||
return false;
|
||||
offset64 = int64_t(uint64_t(offset64) >> discardLsb);
|
||||
}
|
||||
|
||||
value = uint64_t(offset64) & Support::lsbMask<uint64_t>(bitCount);
|
||||
if (value != uint64_t(offset64))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// The rest of OffsetType options are all signed.
|
||||
if (discardLsb) {
|
||||
ASMJIT_ASSERT(discardLsb <= 32);
|
||||
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
|
||||
return false;
|
||||
offset64 >>= discardLsb;
|
||||
}
|
||||
|
||||
if (!Support::isEncodableOffset64(offset64, bitCount))
|
||||
return false;
|
||||
|
||||
value = uint64_t(offset64);
|
||||
}
|
||||
|
||||
switch (format.type()) {
|
||||
case OffsetType::kSignedOffset:
|
||||
case OffsetType::kUnsignedOffset: {
|
||||
*dst = (value & Support::lsbMask<uint64_t>(bitCount)) << format.immBitShift();
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CodeWriterUtils::writeOffset(void* dst, int64_t offset64, const OffsetFormat& format) noexcept {
|
||||
// Offset the destination by ValueOffset so the `dst` points to the
|
||||
// patched word instead of the beginning of the patched region.
|
||||
dst = static_cast<char*>(dst) + format.valueOffset();
|
||||
|
||||
switch (format.valueSize()) {
|
||||
case 1: {
|
||||
uint32_t mask;
|
||||
if (!encodeOffset32(&mask, offset64, format))
|
||||
return false;
|
||||
|
||||
Support::writeU8(dst, uint8_t(Support::readU8(dst) | mask));
|
||||
return true;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
uint32_t mask;
|
||||
if (!encodeOffset32(&mask, offset64, format))
|
||||
return false;
|
||||
|
||||
Support::writeU16uLE(dst, uint16_t(Support::readU16uLE(dst) | mask));
|
||||
return true;
|
||||
}
|
||||
|
||||
case 4: {
|
||||
uint32_t mask;
|
||||
if (!encodeOffset32(&mask, offset64, format)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Support::writeU32uLE(dst, Support::readU32uLE(dst) | mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
case 8: {
|
||||
uint64_t mask;
|
||||
if (!encodeOffset64(&mask, offset64, format))
|
||||
return false;
|
||||
|
||||
Support::writeU64uLE(dst, Support::readU64uLE(dst) | mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
179
lib/lepton/asmjit/core/codewriter_p.h
Normal file
179
lib/lepton/asmjit/core/codewriter_p.h
Normal file
@ -0,0 +1,179 @@
|
||||
// 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_CODEBUFFERWRITER_P_H_INCLUDED
|
||||
#define ASMJIT_CORE_CODEBUFFERWRITER_P_H_INCLUDED
|
||||
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/codebuffer.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_assembler
|
||||
//! \{
|
||||
|
||||
struct OffsetFormat;
|
||||
|
||||
//! Helper that is used to write into a \ref CodeBuffer held by \ref BaseAssembler.
|
||||
class CodeWriter {
|
||||
public:
|
||||
uint8_t* _cursor;
|
||||
|
||||
ASMJIT_FORCE_INLINE explicit CodeWriter(BaseAssembler* a) noexcept
|
||||
: _cursor(a->_bufferPtr) {}
|
||||
|
||||
ASMJIT_FORCE_INLINE Error ensureSpace(BaseAssembler* a, size_t n) noexcept {
|
||||
size_t remainingSpace = (size_t)(a->_bufferEnd - _cursor);
|
||||
if (ASMJIT_UNLIKELY(remainingSpace < n)) {
|
||||
CodeBuffer& buffer = a->_section->_buffer;
|
||||
Error err = a->_code->growBuffer(&buffer, n);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return a->reportError(err);
|
||||
_cursor = a->_bufferPtr;
|
||||
}
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_FORCE_INLINE uint8_t* cursor() const noexcept { return _cursor; }
|
||||
ASMJIT_FORCE_INLINE void setCursor(uint8_t* cursor) noexcept { _cursor = cursor; }
|
||||
ASMJIT_FORCE_INLINE void advance(size_t n) noexcept { _cursor += n; }
|
||||
|
||||
ASMJIT_FORCE_INLINE size_t offsetFrom(uint8_t* from) const noexcept {
|
||||
ASMJIT_ASSERT(_cursor >= from);
|
||||
return (size_t)(_cursor - from);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void emit8(T val) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
_cursor[0] = uint8_t(U(val) & U(0xFF));
|
||||
_cursor++;
|
||||
}
|
||||
|
||||
template<typename T, typename Y>
|
||||
ASMJIT_FORCE_INLINE void emit8If(T val, Y cond) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
ASMJIT_ASSERT(size_t(cond) <= 1u);
|
||||
|
||||
_cursor[0] = uint8_t(U(val) & U(0xFF));
|
||||
_cursor += size_t(cond);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void emit16uLE(T val) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
Support::writeU16uLE(_cursor, uint16_t(U(val) & 0xFFFFu));
|
||||
_cursor += 2;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void emit16uBE(T val) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
Support::writeU16uBE(_cursor, uint16_t(U(val) & 0xFFFFu));
|
||||
_cursor += 2;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void emit32uLE(T val) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
Support::writeU32uLE(_cursor, uint32_t(U(val) & 0xFFFFFFFFu));
|
||||
_cursor += 4;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void emit32uBE(T val) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
Support::writeU32uBE(_cursor, uint32_t(U(val) & 0xFFFFFFFFu));
|
||||
_cursor += 4;
|
||||
}
|
||||
|
||||
ASMJIT_FORCE_INLINE void emitData(const void* data, size_t size) noexcept {
|
||||
ASMJIT_ASSERT(size != 0);
|
||||
memcpy(_cursor, data, size);
|
||||
_cursor += size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void emitValueLE(const T& value, size_t size) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
ASMJIT_ASSERT(size <= sizeof(T));
|
||||
|
||||
U v = U(value);
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
_cursor[i] = uint8_t(v & 0xFFu);
|
||||
v >>= 8;
|
||||
}
|
||||
_cursor += size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void emitValueBE(const T& value, size_t size) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
ASMJIT_ASSERT(size <= sizeof(T));
|
||||
|
||||
U v = U(value);
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
_cursor[i] = uint8_t(v >> (sizeof(T) - 8));
|
||||
v <<= 8;
|
||||
}
|
||||
_cursor += size;
|
||||
}
|
||||
|
||||
ASMJIT_FORCE_INLINE void emitZeros(size_t size) noexcept {
|
||||
ASMJIT_ASSERT(size != 0);
|
||||
memset(_cursor, 0, size);
|
||||
_cursor += size;
|
||||
}
|
||||
|
||||
ASMJIT_FORCE_INLINE void remove8(uint8_t* where) noexcept {
|
||||
ASMJIT_ASSERT(where < _cursor);
|
||||
|
||||
uint8_t* p = where;
|
||||
while (++p != _cursor)
|
||||
p[-1] = p[0];
|
||||
_cursor--;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_FORCE_INLINE void insert8(uint8_t* where, T val) noexcept {
|
||||
uint8_t* p = _cursor;
|
||||
|
||||
while (p != where) {
|
||||
p[0] = p[-1];
|
||||
p--;
|
||||
}
|
||||
|
||||
*p = uint8_t(val & 0xFF);
|
||||
_cursor++;
|
||||
}
|
||||
|
||||
ASMJIT_FORCE_INLINE void done(BaseAssembler* a) noexcept {
|
||||
CodeBuffer& buffer = a->_section->_buffer;
|
||||
size_t newSize = (size_t)(_cursor - a->_bufferData);
|
||||
ASMJIT_ASSERT(newSize <= buffer.capacity());
|
||||
|
||||
a->_bufferPtr = _cursor;
|
||||
buffer._size = Support::max(buffer._size, newSize);
|
||||
}
|
||||
};
|
||||
|
||||
//! Code writer utilities.
|
||||
namespace CodeWriterUtils {
|
||||
|
||||
bool encodeOffset32(uint32_t* dst, int64_t offset64, const OffsetFormat& format) noexcept;
|
||||
bool encodeOffset64(uint64_t* dst, int64_t offset64, const OffsetFormat& format) noexcept;
|
||||
|
||||
bool writeOffset(void* dst, int64_t offset64, const OffsetFormat& format) noexcept;
|
||||
|
||||
} // {CodeWriterUtils}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_CODEBUFFERWRITER_P_H_INCLUDED
|
||||
582
lib/lepton/asmjit/core/compiler.cpp
Normal file
582
lib/lepton/asmjit/core/compiler.cpp
Normal file
@ -0,0 +1,582 @@
|
||||
// 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"
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/compiler.h"
|
||||
#include "../core/cpuinfo.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/rapass_p.h"
|
||||
#include "../core/rastack_p.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// GlobalConstPoolPass
|
||||
// ===================
|
||||
|
||||
class GlobalConstPoolPass : public Pass {
|
||||
public:
|
||||
typedef Pass Base;
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(GlobalConstPoolPass)
|
||||
|
||||
GlobalConstPoolPass() noexcept : Pass("GlobalConstPoolPass") {}
|
||||
|
||||
Error run(Zone* zone, Logger* logger) override {
|
||||
DebugUtils::unused(zone, logger);
|
||||
|
||||
// Flush the global constant pool.
|
||||
BaseCompiler* compiler = static_cast<BaseCompiler*>(_cb);
|
||||
ConstPoolNode* globalConstPool = compiler->_constPools[uint32_t(ConstPoolScope::kGlobal)];
|
||||
|
||||
if (globalConstPool) {
|
||||
compiler->addAfter(globalConstPool, compiler->lastNode());
|
||||
compiler->_constPools[uint32_t(ConstPoolScope::kGlobal)] = nullptr;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
};
|
||||
|
||||
// BaseCompiler - Construction & Destruction
|
||||
// =========================================
|
||||
|
||||
BaseCompiler::BaseCompiler() noexcept
|
||||
: BaseBuilder(),
|
||||
_func(nullptr),
|
||||
_vRegZone(4096 - Zone::kBlockOverhead),
|
||||
_vRegArray(),
|
||||
_constPools { nullptr, nullptr } {
|
||||
_emitterType = EmitterType::kCompiler;
|
||||
_validationFlags = ValidationFlags::kEnableVirtRegs;
|
||||
}
|
||||
BaseCompiler::~BaseCompiler() noexcept {}
|
||||
|
||||
// BaseCompiler - Function Management
|
||||
// ==================================
|
||||
|
||||
Error BaseCompiler::newFuncNode(FuncNode** out, const FuncSignature& signature) {
|
||||
*out = nullptr;
|
||||
|
||||
// Create FuncNode together with all the required surrounding nodes.
|
||||
FuncNode* funcNode;
|
||||
ASMJIT_PROPAGATE(_newNodeT<FuncNode>(&funcNode));
|
||||
ASMJIT_PROPAGATE(newLabelNode(&funcNode->_exitNode));
|
||||
ASMJIT_PROPAGATE(_newNodeT<SentinelNode>(&funcNode->_end, SentinelType::kFuncEnd));
|
||||
|
||||
// Initialize the function's detail info.
|
||||
Error err = funcNode->detail().init(signature, environment());
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
// If the Target guarantees greater stack alignment than required by the calling convention
|
||||
// then override it as we can prevent having to perform dynamic stack alignment
|
||||
uint32_t environmentStackAlignment = _environment.stackAlignment();
|
||||
|
||||
if (funcNode->_funcDetail._callConv.naturalStackAlignment() < environmentStackAlignment)
|
||||
funcNode->_funcDetail._callConv.setNaturalStackAlignment(environmentStackAlignment);
|
||||
|
||||
// Initialize the function frame.
|
||||
err = funcNode->_frame.init(funcNode->_funcDetail);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
// Allocate space for function arguments.
|
||||
funcNode->_args = nullptr;
|
||||
if (funcNode->argCount() != 0) {
|
||||
funcNode->_args = _allocator.allocT<FuncNode::ArgPack>(funcNode->argCount() * sizeof(FuncNode::ArgPack));
|
||||
if (ASMJIT_UNLIKELY(!funcNode->_args))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
memset(funcNode->_args, 0, funcNode->argCount() * sizeof(FuncNode::ArgPack));
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(registerLabelNode(funcNode));
|
||||
|
||||
*out = funcNode;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::addFuncNode(FuncNode** out, const FuncSignature& signature) {
|
||||
ASMJIT_PROPAGATE(newFuncNode(out, signature));
|
||||
ASMJIT_ASSUME(*out != nullptr);
|
||||
|
||||
addFunc(*out);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::newFuncRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1) {
|
||||
uint32_t opCount = !o1.isNone() ? 2u : !o0.isNone() ? 1u : 0u;
|
||||
FuncRetNode* node;
|
||||
|
||||
ASMJIT_PROPAGATE(_newNodeT<FuncRetNode>(&node));
|
||||
ASMJIT_ASSUME(node != nullptr);
|
||||
|
||||
node->setOpCount(opCount);
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->resetOpRange(2, node->opCapacity());
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::addFuncRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1) {
|
||||
ASMJIT_PROPAGATE(newFuncRetNode(out, o0, o1));
|
||||
addNode(*out);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
FuncNode* BaseCompiler::addFunc(FuncNode* func) {
|
||||
_func = func;
|
||||
|
||||
addNode(func); // Function node.
|
||||
BaseNode* prev = cursor(); // {CURSOR}.
|
||||
addNode(func->exitNode()); // Function exit label.
|
||||
addNode(func->endNode()); // Function end sentinel.
|
||||
|
||||
_setCursor(prev);
|
||||
return func;
|
||||
}
|
||||
|
||||
Error BaseCompiler::endFunc() {
|
||||
FuncNode* func = _func;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!func))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidState));
|
||||
|
||||
// Add the local constant pool at the end of the function (if exists).
|
||||
ConstPoolNode* localConstPool = _constPools[uint32_t(ConstPoolScope::kLocal)];
|
||||
if (localConstPool) {
|
||||
setCursor(func->endNode()->prev());
|
||||
addNode(localConstPool);
|
||||
_constPools[uint32_t(ConstPoolScope::kLocal)] = nullptr;
|
||||
}
|
||||
|
||||
// Mark as finished.
|
||||
_func = nullptr;
|
||||
|
||||
SentinelNode* end = func->endNode();
|
||||
setCursor(end);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseCompiler - Function Invocation
|
||||
// ==================================
|
||||
|
||||
Error BaseCompiler::newInvokeNode(InvokeNode** out, InstId instId, const Operand_& o0, const FuncSignature& signature) {
|
||||
InvokeNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<InvokeNode>(&node, instId, InstOptions::kNone));
|
||||
|
||||
node->setOpCount(1);
|
||||
node->setOp(0, o0);
|
||||
node->resetOpRange(1, node->opCapacity());
|
||||
|
||||
Error err = node->detail().init(signature, environment());
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
// Skip the allocation if there are no arguments.
|
||||
uint32_t argCount = signature.argCount();
|
||||
if (argCount) {
|
||||
node->_args = static_cast<InvokeNode::OperandPack*>(_allocator.alloc(argCount * sizeof(InvokeNode::OperandPack)));
|
||||
if (!node->_args)
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
memset(node->_args, 0, argCount * sizeof(InvokeNode::OperandPack));
|
||||
}
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::addInvokeNode(InvokeNode** out, InstId instId, const Operand_& o0, const FuncSignature& signature) {
|
||||
ASMJIT_PROPAGATE(newInvokeNode(out, instId, o0, signature));
|
||||
addNode(*out);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseCompiler - Virtual Registers
|
||||
// ================================
|
||||
|
||||
static void BaseCompiler_assignGenericName(BaseCompiler* self, VirtReg* vReg) {
|
||||
uint32_t index = unsigned(Operand::virtIdToIndex(vReg->_id));
|
||||
|
||||
char buf[64];
|
||||
int size = snprintf(buf, ASMJIT_ARRAY_SIZE(buf), "%%%u", unsigned(index));
|
||||
|
||||
ASMJIT_ASSERT(size > 0 && size < int(ASMJIT_ARRAY_SIZE(buf)));
|
||||
vReg->_name.setData(&self->_dataZone, buf, unsigned(size));
|
||||
}
|
||||
|
||||
Error BaseCompiler::newVirtReg(VirtReg** out, TypeId typeId, OperandSignature signature, const char* name) {
|
||||
*out = nullptr;
|
||||
uint32_t index = _vRegArray.size();
|
||||
|
||||
if (ASMJIT_UNLIKELY(index >= uint32_t(Operand::kVirtIdCount)))
|
||||
return reportError(DebugUtils::errored(kErrorTooManyVirtRegs));
|
||||
|
||||
if (ASMJIT_UNLIKELY(_vRegArray.willGrow(&_allocator) != kErrorOk))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
VirtReg* vReg = _vRegZone.allocZeroedT<VirtReg>();
|
||||
if (ASMJIT_UNLIKELY(!vReg))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
uint32_t size = TypeUtils::sizeOf(typeId);
|
||||
uint32_t alignment = Support::min<uint32_t>(size, 64);
|
||||
|
||||
vReg = new(vReg) VirtReg(signature, Operand::indexToVirtId(index), size, alignment, typeId);
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (name && name[0] != '\0')
|
||||
vReg->_name.setData(&_dataZone, name, SIZE_MAX);
|
||||
else
|
||||
BaseCompiler_assignGenericName(this, vReg);
|
||||
#else
|
||||
DebugUtils::unused(name);
|
||||
#endif
|
||||
|
||||
_vRegArray.appendUnsafe(vReg);
|
||||
*out = vReg;
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newReg(BaseReg* out, TypeId typeId, const char* name) {
|
||||
OperandSignature regSignature;
|
||||
out->reset();
|
||||
|
||||
Error err = ArchUtils::typeIdToRegSignature(arch(), typeId, &typeId, ®Signature);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
VirtReg* vReg;
|
||||
ASMJIT_PROPAGATE(newVirtReg(&vReg, typeId, regSignature, name));
|
||||
ASMJIT_ASSUME(vReg != nullptr);
|
||||
|
||||
out->_initReg(regSignature, vReg->id());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newRegFmt(BaseReg* out, TypeId typeId, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
StringTmp<256> sb;
|
||||
|
||||
va_start(ap, fmt);
|
||||
sb.appendVFormat(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return _newReg(out, typeId, sb.data());
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newReg(BaseReg* out, const BaseReg& ref, const char* name) {
|
||||
out->reset();
|
||||
|
||||
OperandSignature regSignature;
|
||||
TypeId typeId;
|
||||
|
||||
if (isVirtRegValid(ref)) {
|
||||
VirtReg* vRef = virtRegByReg(ref);
|
||||
typeId = vRef->typeId();
|
||||
|
||||
// NOTE: It's possible to cast one register type to another if it's the same register group. However, VirtReg
|
||||
// always contains the TypeId that was used to create the register. This means that in some cases we may end
|
||||
// up having different size of `ref` and `vRef`. In such case we adjust the TypeId to match the `ref` register
|
||||
// type instead of the original register type, which should be the expected behavior.
|
||||
uint32_t typeSize = TypeUtils::sizeOf(typeId);
|
||||
uint32_t refSize = ref.size();
|
||||
|
||||
if (typeSize != refSize) {
|
||||
if (TypeUtils::isInt(typeId)) {
|
||||
// GP register - change TypeId to match `ref`, but keep sign of `vRef`.
|
||||
switch (refSize) {
|
||||
case 1: typeId = TypeId(uint32_t(TypeId::kInt8 ) | (uint32_t(typeId) & 1)); break;
|
||||
case 2: typeId = TypeId(uint32_t(TypeId::kInt16) | (uint32_t(typeId) & 1)); break;
|
||||
case 4: typeId = TypeId(uint32_t(TypeId::kInt32) | (uint32_t(typeId) & 1)); break;
|
||||
case 8: typeId = TypeId(uint32_t(TypeId::kInt64) | (uint32_t(typeId) & 1)); break;
|
||||
default: typeId = TypeId::kVoid; break;
|
||||
}
|
||||
}
|
||||
else if (TypeUtils::isMmx(typeId)) {
|
||||
// MMX register - always use 64-bit.
|
||||
typeId = TypeId::kMmx64;
|
||||
}
|
||||
else if (TypeUtils::isMask(typeId)) {
|
||||
// Mask register - change TypeId to match `ref` size.
|
||||
switch (refSize) {
|
||||
case 1: typeId = TypeId::kMask8; break;
|
||||
case 2: typeId = TypeId::kMask16; break;
|
||||
case 4: typeId = TypeId::kMask32; break;
|
||||
case 8: typeId = TypeId::kMask64; break;
|
||||
default: typeId = TypeId::kVoid; break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Vector register - change TypeId to match `ref` size, keep vector metadata.
|
||||
TypeId scalarTypeId = TypeUtils::scalarOf(typeId);
|
||||
switch (refSize) {
|
||||
case 16: typeId = TypeUtils::scalarToVector(scalarTypeId, TypeId::_kVec128Start); break;
|
||||
case 32: typeId = TypeUtils::scalarToVector(scalarTypeId, TypeId::_kVec256Start); break;
|
||||
case 64: typeId = TypeUtils::scalarToVector(scalarTypeId, TypeId::_kVec512Start); break;
|
||||
default: typeId = TypeId::kVoid; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeId == TypeId::kVoid)
|
||||
return reportError(DebugUtils::errored(kErrorInvalidState));
|
||||
}
|
||||
}
|
||||
else {
|
||||
typeId = ArchTraits::byArch(arch()).regTypeToTypeId(ref.type());
|
||||
}
|
||||
|
||||
Error err = ArchUtils::typeIdToRegSignature(arch(), typeId, &typeId, ®Signature);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
VirtReg* vReg;
|
||||
ASMJIT_PROPAGATE(newVirtReg(&vReg, typeId, regSignature, name));
|
||||
ASMJIT_ASSUME(vReg != nullptr);
|
||||
|
||||
out->_initReg(regSignature, vReg->id());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newRegFmt(BaseReg* out, const BaseReg& ref, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
StringTmp<256> sb;
|
||||
|
||||
va_start(ap, fmt);
|
||||
sb.appendVFormat(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return _newReg(out, ref, sb.data());
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newStack(BaseMem* out, uint32_t size, uint32_t alignment, const char* name) {
|
||||
out->reset();
|
||||
|
||||
if (size == 0)
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
if (alignment == 0)
|
||||
alignment = 1;
|
||||
|
||||
if (!Support::isPowerOf2(alignment))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
if (alignment > 64)
|
||||
alignment = 64;
|
||||
|
||||
VirtReg* vReg;
|
||||
ASMJIT_PROPAGATE(newVirtReg(&vReg, TypeId::kVoid, OperandSignature{0}, name));
|
||||
ASMJIT_ASSUME(vReg != nullptr);
|
||||
|
||||
vReg->_virtSize = size;
|
||||
vReg->_isStack = true;
|
||||
vReg->_alignment = uint8_t(alignment);
|
||||
|
||||
// Set the memory operand to GPD/GPQ and its id to VirtReg.
|
||||
*out = BaseMem(OperandSignature::fromOpType(OperandType::kMem) |
|
||||
OperandSignature::fromMemBaseType(_gpSignature.regType()) |
|
||||
OperandSignature::fromBits(OperandSignature::kMemRegHomeFlag),
|
||||
vReg->id(), 0, 0);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::setStackSize(uint32_t virtId, uint32_t newSize, uint32_t newAlignment) {
|
||||
if (!isVirtIdValid(virtId))
|
||||
return DebugUtils::errored(kErrorInvalidVirtId);
|
||||
|
||||
if (newAlignment && !Support::isPowerOf2(newAlignment))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
if (newAlignment > 64)
|
||||
newAlignment = 64;
|
||||
|
||||
VirtReg* vReg = virtRegById(virtId);
|
||||
if (newSize)
|
||||
vReg->_virtSize = newSize;
|
||||
|
||||
if (newAlignment)
|
||||
vReg->_alignment = uint8_t(newAlignment);
|
||||
|
||||
// This is required if the RAPass is already running. There is a chance that a stack-slot has been already
|
||||
// allocated and in that case it has to be updated as well, otherwise we would allocate wrong amount of memory.
|
||||
RAWorkReg* workReg = vReg->_workReg;
|
||||
if (workReg && workReg->_stackSlot) {
|
||||
workReg->_stackSlot->_size = vReg->_virtSize;
|
||||
workReg->_stackSlot->_alignment = vReg->_alignment;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newConst(BaseMem* out, ConstPoolScope scope, const void* data, size_t size) {
|
||||
out->reset();
|
||||
|
||||
if (uint32_t(scope) > 1)
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
if (!_constPools[uint32_t(scope)])
|
||||
ASMJIT_PROPAGATE(newConstPoolNode(&_constPools[uint32_t(scope)]));
|
||||
|
||||
ConstPoolNode* pool = _constPools[uint32_t(scope)];
|
||||
size_t off;
|
||||
Error err = pool->add(data, size, off);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
*out = BaseMem(OperandSignature::fromOpType(OperandType::kMem) |
|
||||
OperandSignature::fromMemBaseType(RegType::kLabelTag) |
|
||||
OperandSignature::fromSize(uint32_t(size)),
|
||||
pool->labelId(), 0, int32_t(off));
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
void BaseCompiler::rename(const BaseReg& reg, const char* fmt, ...) {
|
||||
if (!reg.isVirtReg()) return;
|
||||
|
||||
VirtReg* vReg = virtRegById(reg.id());
|
||||
if (!vReg) return;
|
||||
|
||||
if (fmt && fmt[0] != '\0') {
|
||||
char buf[128];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
vReg->_name.setData(&_dataZone, buf, SIZE_MAX);
|
||||
}
|
||||
else {
|
||||
BaseCompiler_assignGenericName(this, vReg);
|
||||
}
|
||||
}
|
||||
|
||||
// BaseCompiler - Jump Annotations
|
||||
// ===============================
|
||||
|
||||
Error BaseCompiler::newJumpNode(JumpNode** out, InstId instId, InstOptions instOptions, const Operand_& o0, JumpAnnotation* annotation) {
|
||||
JumpNode* node = _allocator.allocT<JumpNode>();
|
||||
uint32_t opCount = 1;
|
||||
|
||||
*out = node;
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
node = new(node) JumpNode(this, instId, instOptions, opCount, annotation);
|
||||
node->setOp(0, o0);
|
||||
node->resetOpRange(opCount, JumpNode::kBaseOpCapacity);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::emitAnnotatedJump(InstId instId, const Operand_& o0, JumpAnnotation* annotation) {
|
||||
InstOptions options = instOptions() | forcedInstOptions();
|
||||
RegOnly extra = extraReg();
|
||||
const char* comment = inlineComment();
|
||||
|
||||
resetInstOptions();
|
||||
resetInlineComment();
|
||||
resetExtraReg();
|
||||
|
||||
JumpNode* node;
|
||||
ASMJIT_PROPAGATE(newJumpNode(&node, instId, options, o0, annotation));
|
||||
|
||||
node->setExtraReg(extra);
|
||||
if (comment)
|
||||
node->setInlineComment(static_cast<char*>(_dataZone.dup(comment, strlen(comment), true)));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
JumpAnnotation* BaseCompiler::newJumpAnnotation() {
|
||||
if (_jumpAnnotations.grow(&_allocator, 1) != kErrorOk) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t id = _jumpAnnotations.size();
|
||||
JumpAnnotation* jumpAnnotation = _allocator.newT<JumpAnnotation>(this, id);
|
||||
|
||||
if (!jumpAnnotation) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_jumpAnnotations.appendUnsafe(jumpAnnotation);
|
||||
return jumpAnnotation;
|
||||
}
|
||||
|
||||
// BaseCompiler - Events
|
||||
// =====================
|
||||
|
||||
Error BaseCompiler::onAttach(CodeHolder* code) noexcept {
|
||||
ASMJIT_PROPAGATE(Base::onAttach(code));
|
||||
|
||||
const ArchTraits& archTraits = ArchTraits::byArch(code->arch());
|
||||
RegType nativeRegType = Environment::is32Bit(code->arch()) ? RegType::kGp32 : RegType::kGp64;
|
||||
_gpSignature = archTraits.regTypeToSignature(nativeRegType);
|
||||
|
||||
Error err = addPassT<GlobalConstPoolPass>();
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
onDetach(code);
|
||||
return err;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::onDetach(CodeHolder* code) noexcept {
|
||||
_func = nullptr;
|
||||
_constPools[uint32_t(ConstPoolScope::kLocal)] = nullptr;
|
||||
_constPools[uint32_t(ConstPoolScope::kGlobal)] = nullptr;
|
||||
|
||||
_vRegArray.reset();
|
||||
_vRegZone.reset();
|
||||
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
// FuncPass - Construction & Destruction
|
||||
// =====================================
|
||||
|
||||
FuncPass::FuncPass(const char* name) noexcept
|
||||
: Pass(name) {}
|
||||
|
||||
// FuncPass - Run
|
||||
// ==============
|
||||
|
||||
Error FuncPass::run(Zone* zone, Logger* logger) {
|
||||
BaseNode* node = cb()->firstNode();
|
||||
if (!node) return kErrorOk;
|
||||
|
||||
do {
|
||||
if (node->type() == NodeType::kFunc) {
|
||||
FuncNode* func = node->as<FuncNode>();
|
||||
node = func->endNode();
|
||||
ASMJIT_PROPAGATE(runOnFunction(zone, logger, func));
|
||||
}
|
||||
|
||||
// Find a function by skipping all nodes that are not `NodeType::kFunc`.
|
||||
do {
|
||||
node = node->next();
|
||||
} while (node && node->type() != NodeType::kFunc);
|
||||
} while (node);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_COMPILER
|
||||
737
lib/lepton/asmjit/core/compiler.h
Normal file
737
lib/lepton/asmjit/core/compiler.h
Normal file
@ -0,0 +1,737 @@
|
||||
// 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_COMPILER_H_INCLUDED
|
||||
#define ASMJIT_CORE_COMPILER_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/builder.h"
|
||||
#include "../core/constpool.h"
|
||||
#include "../core/compilerdefs.h"
|
||||
#include "../core/func.h"
|
||||
#include "../core/inst.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/zone.h"
|
||||
#include "../core/zonevector.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
class JumpAnnotation;
|
||||
class JumpNode;
|
||||
class FuncNode;
|
||||
class FuncRetNode;
|
||||
class InvokeNode;
|
||||
|
||||
//! \addtogroup asmjit_compiler
|
||||
//! \{
|
||||
|
||||
//! Code emitter that uses virtual registers and performs register allocation.
|
||||
//!
|
||||
//! Compiler is a high-level code-generation tool that provides register allocation and automatic handling of function
|
||||
//! calling conventions. It was primarily designed for merging multiple parts of code into a function without worrying
|
||||
//! about registers and function calling conventions.
|
||||
//!
|
||||
//! BaseCompiler can be used, with a minimum effort, to handle 32-bit and 64-bit code generation within a single code
|
||||
//! base.
|
||||
//!
|
||||
//! BaseCompiler is based on BaseBuilder and contains all the features it provides. It means that the code it stores
|
||||
//! can be modified (removed, added, injected) and analyzed. When the code is finalized the compiler can emit the code
|
||||
//! into an Assembler to translate the abstract representation into a machine code.
|
||||
//!
|
||||
//! Check out architecture specific compilers for more details and examples:
|
||||
//!
|
||||
//! - \ref x86::Compiler - X86/X64 compiler implementation.
|
||||
class ASMJIT_VIRTAPI BaseCompiler : public BaseBuilder {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(BaseCompiler)
|
||||
typedef BaseBuilder Base;
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Current function.
|
||||
FuncNode* _func;
|
||||
//! Allocates `VirtReg` objects.
|
||||
Zone _vRegZone;
|
||||
//! Stores array of `VirtReg` pointers.
|
||||
ZoneVector<VirtReg*> _vRegArray;
|
||||
//! Stores jump annotations.
|
||||
ZoneVector<JumpAnnotation*> _jumpAnnotations;
|
||||
|
||||
//! Local and global constant pools.
|
||||
//!
|
||||
//! Local constant pool is flushed with each function, global constant pool is flushed only by \ref finalize().
|
||||
ConstPoolNode* _constPools[2];
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `BaseCompiler` instance.
|
||||
ASMJIT_API BaseCompiler() noexcept;
|
||||
//! Destroys the `BaseCompiler` instance.
|
||||
ASMJIT_API virtual ~BaseCompiler() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Function Management
|
||||
//! \{
|
||||
|
||||
//! Creates a new \ref FuncNode.
|
||||
ASMJIT_API Error newFuncNode(FuncNode** ASMJIT_NONNULL(out), const FuncSignature& signature);
|
||||
//! Creates a new \ref FuncNode adds it to the instruction stream.
|
||||
ASMJIT_API Error addFuncNode(FuncNode** ASMJIT_NONNULL(out), const FuncSignature& signature);
|
||||
|
||||
//! Creates a new \ref FuncRetNode.
|
||||
ASMJIT_API Error newFuncRetNode(FuncRetNode** ASMJIT_NONNULL(out), const Operand_& o0, const Operand_& o1);
|
||||
//! Creates a new \ref FuncRetNode and adds it to the instruction stream.
|
||||
ASMJIT_API Error addFuncRetNode(FuncRetNode** ASMJIT_NONNULL(out), const Operand_& o0, const Operand_& o1);
|
||||
|
||||
//! Returns the current function.
|
||||
inline FuncNode* func() const noexcept { return _func; }
|
||||
|
||||
//! Creates a new \ref FuncNode with the given `signature` and returns it.
|
||||
inline FuncNode* newFunc(const FuncSignature& signature) {
|
||||
FuncNode* node;
|
||||
newFuncNode(&node, signature);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! Creates a new \ref FuncNode with the given `signature`, adds it to the instruction stream by using
|
||||
//! the \ref addFunc(FuncNode*) overload, and returns it.
|
||||
inline FuncNode* addFunc(const FuncSignature& signature) {
|
||||
FuncNode* node;
|
||||
addFuncNode(&node, signature);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! Adds a function `node` to the instruction stream.
|
||||
ASMJIT_API FuncNode* addFunc(FuncNode* ASMJIT_NONNULL(func));
|
||||
//! Emits a sentinel that marks the end of the current function.
|
||||
ASMJIT_API Error endFunc();
|
||||
|
||||
#if !defined(ASMJIT_NO_DEPRECATED)
|
||||
inline Error _setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg);
|
||||
|
||||
//! Sets a function argument at `argIndex` to `reg`.
|
||||
ASMJIT_DEPRECATED("Setting arguments through Compiler is deprecated, use FuncNode->setArg() instead")
|
||||
inline Error setArg(size_t argIndex, const BaseReg& reg) { return _setArg(argIndex, 0, reg); }
|
||||
|
||||
//! Sets a function argument at `argIndex` at `valueIndex` to `reg`.
|
||||
ASMJIT_DEPRECATED("Setting arguments through Compiler is deprecated, use FuncNode->setArg() instead")
|
||||
inline Error setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg) { return _setArg(argIndex, valueIndex, reg); }
|
||||
#endif
|
||||
|
||||
inline Error addRet(const Operand_& o0, const Operand_& o1) {
|
||||
FuncRetNode* node;
|
||||
return addFuncRetNode(&node, o0, o1);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Function Invocation
|
||||
//! \{
|
||||
|
||||
//! Creates a new \ref InvokeNode.
|
||||
ASMJIT_API Error newInvokeNode(InvokeNode** ASMJIT_NONNULL(out), InstId instId, const Operand_& o0, const FuncSignature& signature);
|
||||
//! Creates a new \ref InvokeNode and adds it to the instruction stream.
|
||||
ASMJIT_API Error addInvokeNode(InvokeNode** ASMJIT_NONNULL(out), InstId instId, const Operand_& o0, const FuncSignature& signature);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Virtual Registers
|
||||
//! \{
|
||||
|
||||
//! Creates a new virtual register representing the given `typeId` and `signature`.
|
||||
//!
|
||||
//! \note This function is public, but it's not generally recommended to be used by AsmJit users, use architecture
|
||||
//! specific `newReg()` functionality instead or functions like \ref _newReg() and \ref _newRegFmt().
|
||||
ASMJIT_API Error newVirtReg(VirtReg** ASMJIT_NONNULL(out), TypeId typeId, OperandSignature signature, const char* name);
|
||||
|
||||
//! Creates a new virtual register of the given `typeId` and stores it to `out` operand.
|
||||
ASMJIT_API Error _newReg(BaseReg* ASMJIT_NONNULL(out), TypeId typeId, const char* name = nullptr);
|
||||
|
||||
//! Creates a new virtual register of the given `typeId` and stores it to `out` operand.
|
||||
//!
|
||||
//! \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, ...);
|
||||
|
||||
//! 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);
|
||||
|
||||
//! Creates a new virtual register compatible with the provided reference register `ref`.
|
||||
//!
|
||||
//! \note This version accepts a snprintf() format `fmt` followed by a variadic arguments.
|
||||
ASMJIT_API Error _newRegFmt(BaseReg* ASMJIT_NONNULL(out), const BaseReg& ref, const char* fmt, ...);
|
||||
|
||||
//! Tests whether the given `id` is a valid virtual register id.
|
||||
inline bool isVirtIdValid(uint32_t id) const noexcept {
|
||||
uint32_t index = Operand::virtIdToIndex(id);
|
||||
return index < _vRegArray.size();
|
||||
}
|
||||
//! Tests whether the given `reg` is a virtual register having a valid id.
|
||||
inline bool isVirtRegValid(const BaseReg& reg) const noexcept {
|
||||
return isVirtIdValid(reg.id());
|
||||
}
|
||||
|
||||
//! Returns \ref VirtReg associated with the given `id`.
|
||||
inline VirtReg* virtRegById(uint32_t id) const noexcept {
|
||||
ASMJIT_ASSERT(isVirtIdValid(id));
|
||||
return _vRegArray[Operand::virtIdToIndex(id)];
|
||||
}
|
||||
|
||||
//! Returns \ref VirtReg associated with the given `reg`.
|
||||
inline VirtReg* virtRegByReg(const BaseReg& reg) const noexcept { return virtRegById(reg.id()); }
|
||||
|
||||
//! Returns \ref VirtReg associated with the given virtual register `index`.
|
||||
//!
|
||||
//! \note This is not the same as virtual register id. The conversion between id and its index is implemented
|
||||
//! by \ref Operand_::virtIdToIndex() and \ref Operand_::indexToVirtId() functions.
|
||||
inline VirtReg* virtRegByIndex(uint32_t index) const noexcept { return _vRegArray[index]; }
|
||||
|
||||
//! Returns an array of all virtual registers managed by the Compiler.
|
||||
inline const ZoneVector<VirtReg*>& virtRegs() const noexcept { return _vRegArray; }
|
||||
|
||||
//! \name Stack
|
||||
//! \{
|
||||
|
||||
//! Creates a new stack of the given `size` and `alignment` and stores it to `out`.
|
||||
//!
|
||||
//! \note `name` can be used to give the stack a name, for debugging purposes.
|
||||
ASMJIT_API Error _newStack(BaseMem* ASMJIT_NONNULL(out), uint32_t size, uint32_t alignment, const char* name = nullptr);
|
||||
|
||||
//! Updates the stack size of a stack created by `_newStack()` by its `virtId`.
|
||||
ASMJIT_API Error setStackSize(uint32_t virtId, uint32_t newSize, uint32_t newAlignment = 0);
|
||||
|
||||
//! Updates the stack size of a stack created by `_newStack()`.
|
||||
inline Error setStackSize(const BaseMem& mem, uint32_t newSize, uint32_t newAlignment = 0) {
|
||||
return setStackSize(mem.id(), newSize, newAlignment);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Constants
|
||||
//! \{
|
||||
|
||||
//! Creates a new constant of the given `scope` (see \ref ConstPoolScope).
|
||||
//!
|
||||
//! This function adds a constant of the given `size` to the built-in \ref ConstPool and stores the reference to that
|
||||
//! constant to the `out` operand.
|
||||
ASMJIT_API Error _newConst(BaseMem* ASMJIT_NONNULL(out), ConstPoolScope scope, const void* data, size_t size);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Miscellaneous
|
||||
//! \{
|
||||
|
||||
//! Rename the given virtual register `reg` to a formatted string `fmt`.
|
||||
ASMJIT_API void rename(const BaseReg& reg, const char* fmt, ...);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Jump Annotations
|
||||
//! \{
|
||||
|
||||
inline const ZoneVector<JumpAnnotation*>& jumpAnnotations() const noexcept {
|
||||
return _jumpAnnotations;
|
||||
}
|
||||
|
||||
ASMJIT_API Error newJumpNode(JumpNode** ASMJIT_NONNULL(out), InstId instId, InstOptions instOptions, const Operand_& o0, JumpAnnotation* annotation);
|
||||
ASMJIT_API Error emitAnnotatedJump(InstId instId, const Operand_& o0, JumpAnnotation* annotation);
|
||||
|
||||
//! Returns a new `JumpAnnotation` instance, which can be used to aggregate possible targets of a jump where the
|
||||
//! target is not a label, for example to implement jump tables.
|
||||
ASMJIT_API JumpAnnotation* newJumpAnnotation();
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
|
||||
ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Jump annotation used to annotate jumps.
|
||||
//!
|
||||
//! \ref BaseCompiler allows to emit jumps where the target is either register or memory operand. Such jumps cannot be
|
||||
//! trivially inspected, so instead of doing heuristics AsmJit allows to annotate such jumps with possible targets.
|
||||
//! Register allocator then uses the annotation to construct control-flow, which is then used by liveness analysis and
|
||||
//! other tools to prepare ground for register allocation.
|
||||
class JumpAnnotation {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(JumpAnnotation)
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Compiler that owns this JumpAnnotation.
|
||||
BaseCompiler* _compiler;
|
||||
//! Annotation identifier.
|
||||
uint32_t _annotationId;
|
||||
//! Vector of label identifiers, see \ref labelIds().
|
||||
ZoneVector<uint32_t> _labelIds;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
inline JumpAnnotation(BaseCompiler* ASMJIT_NONNULL(compiler), uint32_t annotationId) noexcept
|
||||
: _compiler(compiler),
|
||||
_annotationId(annotationId) {}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the compiler that owns this JumpAnnotation.
|
||||
inline BaseCompiler* compiler() const noexcept { return _compiler; }
|
||||
//! Returns the annotation id.
|
||||
inline uint32_t annotationId() const noexcept { return _annotationId; }
|
||||
//! Returns a vector of label identifiers that lists all targets of the jump.
|
||||
const ZoneVector<uint32_t>& labelIds() const noexcept { return _labelIds; }
|
||||
|
||||
//! Tests whether the given `label` is a target of this JumpAnnotation.
|
||||
inline bool hasLabel(const Label& label) const noexcept { return hasLabelId(label.id()); }
|
||||
//! Tests whether the given `labelId` is a target of this JumpAnnotation.
|
||||
inline bool hasLabelId(uint32_t labelId) const noexcept { return _labelIds.contains(labelId); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Annotation Building API
|
||||
//! \{
|
||||
|
||||
//! Adds the `label` to the list of targets of this JumpAnnotation.
|
||||
inline Error addLabel(const Label& label) noexcept { return addLabelId(label.id()); }
|
||||
//! Adds the `labelId` to the list of targets of this JumpAnnotation.
|
||||
inline Error addLabelId(uint32_t labelId) noexcept { return _labelIds.append(&_compiler->_allocator, labelId); }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Jump instruction with \ref JumpAnnotation.
|
||||
//!
|
||||
//! \note This node should be only used to represent jump where the jump target cannot be deduced by examining
|
||||
//! instruction operands. For example if the jump target is register or memory location. This pattern is often
|
||||
//! used to perform indirect jumps that use jump table, e.g. to implement `switch{}` statement.
|
||||
class JumpNode : public InstNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(JumpNode)
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
JumpAnnotation* _annotation;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
inline JumpNode(BaseCompiler* ASMJIT_NONNULL(cc), InstId instId, InstOptions options, uint32_t opCount, JumpAnnotation* annotation) noexcept
|
||||
: InstNode(cc, instId, options, opCount, kBaseOpCapacity),
|
||||
_annotation(annotation) {
|
||||
setType(NodeType::kJump);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Tests whether this JumpNode has associated a \ref JumpAnnotation.
|
||||
inline bool hasAnnotation() const noexcept { return _annotation != nullptr; }
|
||||
//! Returns the \ref JumpAnnotation associated with this jump, or `nullptr`.
|
||||
inline JumpAnnotation* annotation() const noexcept { return _annotation; }
|
||||
//! Sets the \ref JumpAnnotation associated with this jump to `annotation`.
|
||||
inline void setAnnotation(JumpAnnotation* annotation) noexcept { _annotation = annotation; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Function node represents a function used by \ref BaseCompiler.
|
||||
//!
|
||||
//! A function is composed of the following:
|
||||
//!
|
||||
//! - Function entry, \ref FuncNode acts as a label, so the entry is implicit. To get the entry, simply use
|
||||
//! \ref FuncNode::label(), which is the same as \ref LabelNode::label().
|
||||
//!
|
||||
//! - Function exit, which is represented by \ref FuncNode::exitNode(). A helper function
|
||||
//! \ref FuncNode::exitLabel() exists and returns an exit label instead of node.
|
||||
//!
|
||||
//! - Function \ref FuncNode::endNode() sentinel. This node marks the end of a function - there should be no
|
||||
//! code that belongs to the function after this node, but the Compiler doesn't enforce that at the moment.
|
||||
//!
|
||||
//! - Function detail, see \ref FuncNode::detail().
|
||||
//!
|
||||
//! - Function frame, see \ref FuncNode::frame().
|
||||
//!
|
||||
//! - Function arguments mapped to virtual registers, see \ref FuncNode::argPacks().
|
||||
//!
|
||||
//! In a node list, the function and its body looks like the following:
|
||||
//!
|
||||
//! \code{.unparsed}
|
||||
//! [...] - Anything before the function.
|
||||
//!
|
||||
//! [FuncNode] - Entry point of the function, acts as a label as well.
|
||||
//! <Prolog> - Prolog inserted by the register allocator.
|
||||
//! {...} - Function body - user code basically.
|
||||
//! [ExitLabel] - Exit label
|
||||
//! <Epilog> - Epilog inserted by the register allocator.
|
||||
//! <Return> - Return inserted by the register allocator.
|
||||
//! {...} - Can contain data or user code (error handling, special cases, ...).
|
||||
//! [FuncEnd] - End sentinel
|
||||
//!
|
||||
//! [...] - Anything after the function.
|
||||
//! \endcode
|
||||
//!
|
||||
//! When a function is added to the instruction stream by \ref BaseCompiler::addFunc() it actually inserts 3 nodes
|
||||
//! (FuncNode, ExitLabel, and FuncEnd) and sets the current cursor to be FuncNode. When \ref BaseCompiler::endFunc()
|
||||
//! is called the cursor is set to FuncEnd. This guarantees that user can use ExitLabel as a marker after additional
|
||||
//! code or data can be placed, which is a common practice.
|
||||
class FuncNode : public LabelNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(FuncNode)
|
||||
|
||||
//! Arguments pack.
|
||||
struct ArgPack {
|
||||
RegOnly _data[Globals::kMaxValuePack];
|
||||
|
||||
inline void reset() noexcept {
|
||||
for (size_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++)
|
||||
_data[valueIndex].reset();
|
||||
}
|
||||
|
||||
inline RegOnly& operator[](size_t valueIndex) noexcept { return _data[valueIndex]; }
|
||||
inline const RegOnly& operator[](size_t valueIndex) const noexcept { return _data[valueIndex]; }
|
||||
};
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Function detail.
|
||||
FuncDetail _funcDetail;
|
||||
//! Function frame.
|
||||
FuncFrame _frame;
|
||||
//! Function exit label.
|
||||
LabelNode* _exitNode;
|
||||
//! Function end (sentinel).
|
||||
SentinelNode* _end;
|
||||
//! Argument packs.
|
||||
ArgPack* _args;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `FuncNode` instance.
|
||||
//!
|
||||
//! Always use `BaseCompiler::addFunc()` to create a new `FuncNode`.
|
||||
inline FuncNode(BaseBuilder* ASMJIT_NONNULL(cb)) noexcept
|
||||
: LabelNode(cb),
|
||||
_funcDetail(),
|
||||
_frame(),
|
||||
_exitNode(nullptr),
|
||||
_end(nullptr),
|
||||
_args(nullptr) {
|
||||
setType(NodeType::kFunc);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \{
|
||||
//! \name Accessors
|
||||
|
||||
//! Returns function exit `LabelNode`.
|
||||
inline LabelNode* exitNode() const noexcept { return _exitNode; }
|
||||
//! Returns function exit label.
|
||||
inline Label exitLabel() const noexcept { return _exitNode->label(); }
|
||||
|
||||
//! Returns "End of Func" sentinel node.
|
||||
inline SentinelNode* endNode() const noexcept { return _end; }
|
||||
|
||||
//! Returns function detail.
|
||||
inline FuncDetail& detail() noexcept { return _funcDetail; }
|
||||
//! Returns function detail.
|
||||
inline const FuncDetail& detail() const noexcept { return _funcDetail; }
|
||||
|
||||
//! Returns function frame.
|
||||
inline FuncFrame& frame() noexcept { return _frame; }
|
||||
//! Returns function frame.
|
||||
inline const FuncFrame& frame() const noexcept { return _frame; }
|
||||
|
||||
//! Returns function attributes.
|
||||
inline FuncAttributes attributes() const noexcept { return _frame.attributes(); }
|
||||
//! Adds `attrs` to the function attributes.
|
||||
inline void addAttributes(FuncAttributes attrs) noexcept { _frame.addAttributes(attrs); }
|
||||
|
||||
//! Returns arguments count.
|
||||
inline uint32_t argCount() const noexcept { return _funcDetail.argCount(); }
|
||||
//! Returns argument packs.
|
||||
inline ArgPack* argPacks() const noexcept { return _args; }
|
||||
|
||||
//! Tests whether the function has a return value.
|
||||
inline bool hasRet() const noexcept { return _funcDetail.hasRet(); }
|
||||
|
||||
//! Returns argument pack at `argIndex`.
|
||||
inline ArgPack& argPack(size_t argIndex) const noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
return _args[argIndex];
|
||||
}
|
||||
|
||||
//! Sets argument at `argIndex`.
|
||||
inline void setArg(size_t argIndex, const BaseReg& vReg) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
_args[argIndex][0].init(vReg);
|
||||
}
|
||||
|
||||
//! \overload
|
||||
inline void setArg(size_t argIndex, const RegOnly& vReg) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
_args[argIndex][0].init(vReg);
|
||||
}
|
||||
|
||||
//! Sets argument at `argIndex` and `valueIndex`.
|
||||
inline void setArg(size_t argIndex, size_t valueIndex, const BaseReg& vReg) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
_args[argIndex][valueIndex].init(vReg);
|
||||
}
|
||||
|
||||
//! \overload
|
||||
inline void setArg(size_t argIndex, size_t valueIndex, const RegOnly& vReg) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
_args[argIndex][valueIndex].init(vReg);
|
||||
}
|
||||
|
||||
//! Resets argument pack at `argIndex`.
|
||||
inline void resetArg(size_t argIndex) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
_args[argIndex].reset();
|
||||
}
|
||||
|
||||
//! Resets argument pack at `argIndex`.
|
||||
inline void resetArg(size_t argIndex, size_t valueIndex) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
_args[argIndex][valueIndex].reset();
|
||||
}
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Function return, used by \ref BaseCompiler.
|
||||
class FuncRetNode : public InstNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(FuncRetNode)
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `FuncRetNode` instance.
|
||||
inline FuncRetNode(BaseBuilder* ASMJIT_NONNULL(cb)) noexcept : InstNode(cb, BaseInst::kIdAbstract, InstOptions::kNone, 0) {
|
||||
_any._nodeType = NodeType::kFuncRet;
|
||||
}
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Function invocation, used by \ref BaseCompiler.
|
||||
class InvokeNode : public InstNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(InvokeNode)
|
||||
|
||||
//! Operand pack provides multiple operands that can be associated with a single return value of function
|
||||
//! argument. Sometims this is necessary to express an argument or return value that requires multiple
|
||||
//! registers, for example 64-bit value in 32-bit mode or passing / returning homogeneous data structures.
|
||||
struct OperandPack {
|
||||
//! Operands.
|
||||
Operand_ _data[Globals::kMaxValuePack];
|
||||
|
||||
//! Reset the pack by resetting all operands in the pack.
|
||||
inline void reset() noexcept {
|
||||
for (size_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++)
|
||||
_data[valueIndex].reset();
|
||||
}
|
||||
|
||||
//! Returns an operand at the given `valueIndex`.
|
||||
inline Operand& operator[](size_t valueIndex) noexcept {
|
||||
ASMJIT_ASSERT(valueIndex < Globals::kMaxValuePack);
|
||||
return _data[valueIndex].as<Operand>();
|
||||
}
|
||||
|
||||
//! Returns an operand at the given `valueIndex` (const).
|
||||
const inline Operand& operator[](size_t valueIndex) const noexcept {
|
||||
ASMJIT_ASSERT(valueIndex < Globals::kMaxValuePack);
|
||||
return _data[valueIndex].as<Operand>();
|
||||
}
|
||||
};
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Function detail.
|
||||
FuncDetail _funcDetail;
|
||||
//! Function return value(s).
|
||||
OperandPack _rets;
|
||||
//! Function arguments.
|
||||
OperandPack* _args;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `InvokeNode` instance.
|
||||
inline InvokeNode(BaseBuilder* ASMJIT_NONNULL(cb), InstId instId, InstOptions options) noexcept
|
||||
: InstNode(cb, instId, options, kBaseOpCapacity),
|
||||
_funcDetail(),
|
||||
_args(nullptr) {
|
||||
setType(NodeType::kInvoke);
|
||||
_resetOps();
|
||||
_rets.reset();
|
||||
addFlags(NodeFlags::kIsRemovable);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Sets the function signature.
|
||||
inline Error init(const FuncSignature& signature, const Environment& environment) noexcept {
|
||||
return _funcDetail.init(signature, environment);
|
||||
}
|
||||
|
||||
//! Returns the function detail.
|
||||
inline FuncDetail& detail() noexcept { return _funcDetail; }
|
||||
//! Returns the function detail.
|
||||
inline const FuncDetail& detail() const noexcept { return _funcDetail; }
|
||||
|
||||
//! Returns the target operand.
|
||||
inline Operand& target() noexcept { return _opArray[0].as<Operand>(); }
|
||||
//! \overload
|
||||
inline const Operand& target() const noexcept { return _opArray[0].as<Operand>(); }
|
||||
|
||||
//! Returns the number of function return values.
|
||||
inline bool hasRet() const noexcept { return _funcDetail.hasRet(); }
|
||||
//! Returns the number of function arguments.
|
||||
inline uint32_t argCount() const noexcept { return _funcDetail.argCount(); }
|
||||
|
||||
//! Returns operand pack representing function return value(s).
|
||||
inline OperandPack& retPack() noexcept { return _rets; }
|
||||
//! Returns operand pack representing function return value(s).
|
||||
inline const OperandPack& retPack() const noexcept { return _rets; }
|
||||
|
||||
//! Returns the return value at the given `valueIndex`.
|
||||
inline Operand& ret(size_t valueIndex = 0) noexcept { return _rets[valueIndex]; }
|
||||
//! \overload
|
||||
inline const Operand& ret(size_t valueIndex = 0) const noexcept { return _rets[valueIndex]; }
|
||||
|
||||
//! Returns operand pack representing function return value(s).
|
||||
inline OperandPack& argPack(size_t argIndex) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
return _args[argIndex];
|
||||
}
|
||||
//! \overload
|
||||
inline const OperandPack& argPack(size_t argIndex) const noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
return _args[argIndex];
|
||||
}
|
||||
|
||||
//! Returns a function argument at the given `argIndex`.
|
||||
inline Operand& arg(size_t argIndex, size_t valueIndex) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
return _args[argIndex][valueIndex];
|
||||
}
|
||||
//! \overload
|
||||
inline const Operand& arg(size_t argIndex, size_t valueIndex) const noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
return _args[argIndex][valueIndex];
|
||||
}
|
||||
|
||||
//! Sets the function return value at `i` to `op`.
|
||||
inline void _setRet(size_t valueIndex, const Operand_& op) noexcept { _rets[valueIndex] = op; }
|
||||
//! Sets the function argument at `i` to `op`.
|
||||
inline void _setArg(size_t argIndex, size_t valueIndex, const Operand_& op) noexcept {
|
||||
ASMJIT_ASSERT(argIndex < argCount());
|
||||
_args[argIndex][valueIndex] = op;
|
||||
}
|
||||
|
||||
//! Sets the function return value at `valueIndex` to `reg`.
|
||||
inline void setRet(size_t valueIndex, const BaseReg& reg) noexcept { _setRet(valueIndex, reg); }
|
||||
|
||||
//! Sets the first function argument in a value-pack at `argIndex` to `reg`.
|
||||
inline void setArg(size_t argIndex, const BaseReg& reg) noexcept { _setArg(argIndex, 0, reg); }
|
||||
//! Sets the first function argument in a value-pack at `argIndex` to `imm`.
|
||||
inline void setArg(size_t argIndex, const Imm& imm) noexcept { _setArg(argIndex, 0, imm); }
|
||||
|
||||
//! Sets the function argument at `argIndex` and `valueIndex` to `reg`.
|
||||
inline void setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg) noexcept { _setArg(argIndex, valueIndex, reg); }
|
||||
//! Sets the function argument at `argIndex` and `valueIndex` to `imm`.
|
||||
inline void setArg(size_t argIndex, size_t valueIndex, const Imm& imm) noexcept { _setArg(argIndex, valueIndex, imm); }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Function pass extends \ref Pass with \ref FuncPass::runOnFunction().
|
||||
class ASMJIT_VIRTAPI FuncPass : public Pass {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(FuncPass)
|
||||
typedef Pass Base;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API FuncPass(const char* name) noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the associated `BaseCompiler`.
|
||||
inline BaseCompiler* cc() const noexcept { return static_cast<BaseCompiler*>(_cb); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Pass Interface
|
||||
//! \{
|
||||
|
||||
//! Calls `runOnFunction()` on each `FuncNode` node found.
|
||||
ASMJIT_API Error run(Zone* zone, Logger* logger) override;
|
||||
|
||||
//! Called once per `FuncNode`.
|
||||
virtual Error runOnFunction(Zone* zone, Logger* logger, FuncNode* func) = 0;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
#if !defined(ASMJIT_NO_DEPRECATED)
|
||||
inline Error BaseCompiler::_setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg) {
|
||||
FuncNode* func = _func;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!func))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidState));
|
||||
|
||||
func->setArg(argIndex, valueIndex, reg);
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_COMPILER
|
||||
#endif // ASMJIT_CORE_COMPILER_H_INCLUDED
|
||||
173
lib/lepton/asmjit/core/compilerdefs.h
Normal file
173
lib/lepton/asmjit/core/compilerdefs.h
Normal file
@ -0,0 +1,173 @@
|
||||
// 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_COMPILERDEFS_H_INCLUDED
|
||||
#define ASMJIT_CORE_COMPILERDEFS_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/type.h"
|
||||
#include "../core/zonestring.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
class RAWorkReg;
|
||||
|
||||
//! \addtogroup asmjit_compiler
|
||||
//! \{
|
||||
|
||||
//! Virtual register data, managed by \ref BaseCompiler.
|
||||
class VirtReg {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(VirtReg)
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Virtual register signature.
|
||||
OperandSignature _signature {};
|
||||
//! Virtual register id.
|
||||
uint32_t _id = 0;
|
||||
//! Virtual register size (can be smaller than `_signature._size`).
|
||||
uint32_t _virtSize = 0;
|
||||
//! Virtual register alignment (for spilling).
|
||||
uint8_t _alignment = 0;
|
||||
//! Type-id.
|
||||
TypeId _typeId = TypeId::kVoid;
|
||||
//! Virtual register weight for alloc/spill decisions.
|
||||
uint8_t _weight = 1;
|
||||
//! True if this is a fixed register, never reallocated.
|
||||
uint8_t _isFixed : 1;
|
||||
//! True if the virtual register is only used as a stack (never accessed as register).
|
||||
uint8_t _isStack : 1;
|
||||
//! True if this virtual register has assigned stack offset (can be only valid after register allocation pass).
|
||||
uint8_t _hasStackSlot : 1;
|
||||
uint8_t _reservedBits : 5;
|
||||
|
||||
//! Stack offset assigned by the register allocator relative to stack pointer (can be negative as well).
|
||||
int32_t _stackOffset = 0;
|
||||
|
||||
//! Reserved for future use (padding).
|
||||
uint32_t _reservedU32 = 0;
|
||||
|
||||
//! Virtual register name (user provided or automatically generated).
|
||||
ZoneString<16> _name {};
|
||||
|
||||
// The following members are used exclusively by RAPass. They are initialized when the VirtReg is created to
|
||||
// null pointers and then changed during RAPass execution. RAPass sets them back to NULL before it returns.
|
||||
|
||||
//! Reference to `RAWorkReg`, used during register allocation.
|
||||
RAWorkReg* _workReg = nullptr;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
inline VirtReg(OperandSignature signature, uint32_t id, uint32_t virtSize, uint32_t alignment, TypeId typeId) noexcept
|
||||
: _signature(signature),
|
||||
_id(id),
|
||||
_virtSize(virtSize),
|
||||
_alignment(uint8_t(alignment)),
|
||||
_typeId(typeId),
|
||||
_isFixed(false),
|
||||
_isStack(false),
|
||||
_hasStackSlot(false),
|
||||
_reservedBits(0),
|
||||
_stackOffset(0),
|
||||
_reservedU32(0) {}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the virtual register id.
|
||||
inline uint32_t id() const noexcept { return _id; }
|
||||
|
||||
//! Returns the virtual register name.
|
||||
inline const char* name() const noexcept { return _name.data(); }
|
||||
//! Returns the size of the virtual register name.
|
||||
inline uint32_t nameSize() const noexcept { return _name.size(); }
|
||||
|
||||
//! Returns a register signature of this virtual register.
|
||||
inline OperandSignature signature() const noexcept { return _signature; }
|
||||
//! Returns a virtual register type (maps to the physical register type as well).
|
||||
inline RegType type() const noexcept { return _signature.regType(); }
|
||||
//! Returns a virtual register group (maps to the physical register group as well).
|
||||
inline RegGroup group() const noexcept { return _signature.regGroup(); }
|
||||
|
||||
//! Returns a real size of the register this virtual register maps to.
|
||||
//!
|
||||
//! For example if this is a 128-bit SIMD register used for a scalar single precision floating point value then
|
||||
//! its virtSize would be 4, however, the `regSize` would still say 16 (128-bits), because it's the smallest size
|
||||
//! of that register type.
|
||||
inline uint32_t regSize() const noexcept { return _signature.size(); }
|
||||
|
||||
//! Returns the virtual register size.
|
||||
//!
|
||||
//! The virtual register size describes how many bytes the virtual register needs to store its content. It can be
|
||||
//! smaller than the physical register size, see `regSize()`.
|
||||
inline uint32_t virtSize() const noexcept { return _virtSize; }
|
||||
|
||||
//! Returns the virtual register alignment.
|
||||
inline uint32_t alignment() const noexcept { return _alignment; }
|
||||
|
||||
//! Returns the virtual register type id.
|
||||
inline TypeId typeId() const noexcept { return _typeId; }
|
||||
|
||||
//! Returns the virtual register weight - the register allocator can use it as explicit hint for alloc/spill
|
||||
//! decisions.
|
||||
inline uint32_t weight() const noexcept { return _weight; }
|
||||
//! Sets the virtual register weight (0 to 255) - the register allocator can use it as explicit hint for
|
||||
//! alloc/spill decisions and initial bin-packing.
|
||||
inline void setWeight(uint32_t weight) noexcept { _weight = uint8_t(weight); }
|
||||
|
||||
//! Returns whether the virtual register is always allocated to a fixed physical register (and never reallocated).
|
||||
//!
|
||||
//! \note This is only used for special purposes and it's mostly internal.
|
||||
inline bool isFixed() const noexcept { return bool(_isFixed); }
|
||||
|
||||
//! Tests whether the virtual register is in fact a stack that only uses the virtual register id.
|
||||
//!
|
||||
//! \note It's an error if a stack is accessed as a register.
|
||||
inline bool isStack() const noexcept { return bool(_isStack); }
|
||||
|
||||
//! Tests whether this virtual register (or stack) has assigned a stack offset.
|
||||
//!
|
||||
//! If this is a virtual register that was never allocated on stack, it would return false, otherwise if
|
||||
//! it's a virtual register that was spilled or explicitly allocated stack, the return value would be true.
|
||||
inline bool hasStackSlot() const noexcept { return bool(_hasStackSlot); }
|
||||
|
||||
//! Assigns a stack offset of this virtual register to `stackOffset` and sets `_hasStackSlot` to true.
|
||||
inline void assignStackSlot(int32_t stackOffset) noexcept {
|
||||
_hasStackSlot = 1;
|
||||
_stackOffset = stackOffset;
|
||||
}
|
||||
|
||||
//! Returns a stack offset associated with a virtual register or explicit stack allocation.
|
||||
//!
|
||||
//! \note Always verify that the stack offset has been assigned by calling \ref hasStackSlot(). The return
|
||||
//! value will be zero when the stack offset was not assigned.
|
||||
inline int32_t stackOffset() const noexcept { return _stackOffset; }
|
||||
|
||||
//! Tests whether the virtual register has an associated `RAWorkReg` at the moment.
|
||||
inline bool hasWorkReg() const noexcept { return _workReg != nullptr; }
|
||||
//! Returns an associated RAWorkReg with this virtual register (only valid during register allocation).
|
||||
inline RAWorkReg* workReg() const noexcept { return _workReg; }
|
||||
//! Associates a RAWorkReg with this virtual register (used by register allocator).
|
||||
inline void setWorkReg(RAWorkReg* workReg) noexcept { _workReg = workReg; }
|
||||
//! Reset the RAWorkReg association (used by register allocator).
|
||||
inline void resetWorkReg() noexcept { _workReg = nullptr; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_COMPILERDEFS_H_INCLUDED
|
||||
|
||||
363
lib/lepton/asmjit/core/constpool.cpp
Normal file
363
lib/lepton/asmjit/core/constpool.cpp
Normal file
@ -0,0 +1,363 @@
|
||||
// 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/constpool.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// ConstPool - Construction & Destruction
|
||||
// ======================================
|
||||
|
||||
ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); }
|
||||
ConstPool::~ConstPool() noexcept {}
|
||||
|
||||
// ConstPool - Reset
|
||||
// =================
|
||||
|
||||
void ConstPool::reset(Zone* zone) noexcept {
|
||||
_zone = zone;
|
||||
|
||||
size_t dataSize = 1;
|
||||
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
|
||||
_tree[i].reset();
|
||||
_tree[i].setDataSize(dataSize);
|
||||
_gaps[i] = nullptr;
|
||||
dataSize <<= 1;
|
||||
}
|
||||
|
||||
_gapPool = nullptr;
|
||||
_size = 0;
|
||||
_alignment = 0;
|
||||
_minItemSize = 0;
|
||||
}
|
||||
|
||||
// ConstPool - Operations
|
||||
// ======================
|
||||
|
||||
static inline ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept {
|
||||
ConstPool::Gap* gap = self->_gapPool;
|
||||
if (!gap)
|
||||
return self->_zone->allocT<ConstPool::Gap>();
|
||||
|
||||
self->_gapPool = gap->_next;
|
||||
return gap;
|
||||
}
|
||||
|
||||
static inline void ConstPool_freeGap(ConstPool* self, ConstPool::Gap* gap) noexcept {
|
||||
gap->_next = self->_gapPool;
|
||||
self->_gapPool = gap;
|
||||
}
|
||||
|
||||
static void ConstPool_addGap(ConstPool* self, size_t offset, size_t size) noexcept {
|
||||
ASMJIT_ASSERT(size > 0);
|
||||
|
||||
while (size > 0) {
|
||||
size_t gapIndex;
|
||||
size_t gapSize;
|
||||
|
||||
if (size >= 32 && Support::isAligned<size_t>(offset, 32)) {
|
||||
gapIndex = ConstPool::kIndex32;
|
||||
gapSize = 32;
|
||||
}
|
||||
else if (size >= 16 && Support::isAligned<size_t>(offset, 16)) {
|
||||
gapIndex = ConstPool::kIndex16;
|
||||
gapSize = 16;
|
||||
}
|
||||
else if (size >= 8 && Support::isAligned<size_t>(offset, 8)) {
|
||||
gapIndex = ConstPool::kIndex8;
|
||||
gapSize = 8;
|
||||
}
|
||||
else if (size >= 4 && Support::isAligned<size_t>(offset, 4)) {
|
||||
gapIndex = ConstPool::kIndex4;
|
||||
gapSize = 4;
|
||||
}
|
||||
else if (size >= 2 && Support::isAligned<size_t>(offset, 2)) {
|
||||
gapIndex = ConstPool::kIndex2;
|
||||
gapSize = 2;
|
||||
}
|
||||
else {
|
||||
gapIndex = ConstPool::kIndex1;
|
||||
gapSize = 1;
|
||||
}
|
||||
|
||||
// We don't have to check for errors here, if this failed nothing really happened (just the gap won't be
|
||||
// visible) and it will fail again at place where the same check would generate `kErrorOutOfMemory` error.
|
||||
ConstPool::Gap* gap = ConstPool_allocGap(self);
|
||||
if (!gap)
|
||||
return;
|
||||
|
||||
gap->_next = self->_gaps[gapIndex];
|
||||
self->_gaps[gapIndex] = gap;
|
||||
|
||||
gap->_offset = offset;
|
||||
gap->_size = gapSize;
|
||||
|
||||
offset += gapSize;
|
||||
size -= gapSize;
|
||||
}
|
||||
}
|
||||
|
||||
Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept {
|
||||
size_t treeIndex;
|
||||
|
||||
if (size == 64)
|
||||
treeIndex = kIndex64;
|
||||
else if (size == 32)
|
||||
treeIndex = kIndex32;
|
||||
else if (size == 16)
|
||||
treeIndex = kIndex16;
|
||||
else if (size == 8)
|
||||
treeIndex = kIndex8;
|
||||
else if (size == 4)
|
||||
treeIndex = kIndex4;
|
||||
else if (size == 2)
|
||||
treeIndex = kIndex2;
|
||||
else if (size == 1)
|
||||
treeIndex = kIndex1;
|
||||
else
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
|
||||
ConstPool::Node* node = _tree[treeIndex].get(data);
|
||||
if (node) {
|
||||
dstOffset = node->_offset;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// Before incrementing the current offset try if there is a gap that can be used for the requested data.
|
||||
size_t offset = ~size_t(0);
|
||||
size_t gapIndex = treeIndex;
|
||||
|
||||
while (gapIndex != kIndexCount - 1) {
|
||||
ConstPool::Gap* gap = _gaps[treeIndex];
|
||||
|
||||
// Check if there is a gap.
|
||||
if (gap) {
|
||||
size_t gapOffset = gap->_offset;
|
||||
size_t gapSize = gap->_size;
|
||||
|
||||
// Destroy the gap for now.
|
||||
_gaps[treeIndex] = gap->_next;
|
||||
ConstPool_freeGap(this, gap);
|
||||
|
||||
offset = gapOffset;
|
||||
ASMJIT_ASSERT(Support::isAligned<size_t>(offset, size));
|
||||
|
||||
gapSize -= size;
|
||||
if (gapSize > 0)
|
||||
ConstPool_addGap(this, gapOffset, gapSize);
|
||||
}
|
||||
|
||||
gapIndex++;
|
||||
}
|
||||
|
||||
if (offset == ~size_t(0)) {
|
||||
// Get how many bytes have to be skipped so the address is aligned accordingly to the 'size'.
|
||||
size_t diff = Support::alignUpDiff<size_t>(_size, size);
|
||||
|
||||
if (diff != 0) {
|
||||
ConstPool_addGap(this, _size, diff);
|
||||
_size += diff;
|
||||
}
|
||||
|
||||
offset = _size;
|
||||
_size += size;
|
||||
}
|
||||
|
||||
// Add the initial node to the right index.
|
||||
node = ConstPool::Tree::_newNode(_zone, data, size, offset, false);
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
_tree[treeIndex].insert(node);
|
||||
_alignment = Support::max<size_t>(_alignment, size);
|
||||
|
||||
dstOffset = offset;
|
||||
|
||||
// Now create a bunch of shared constants that are based on the data pattern. We stop at size 4,
|
||||
// it probably doesn't make sense to split constants down to 1 byte.
|
||||
size_t pCount = 1;
|
||||
size_t smallerSize = size;
|
||||
|
||||
while (smallerSize > 4) {
|
||||
pCount <<= 1;
|
||||
smallerSize >>= 1;
|
||||
|
||||
ASMJIT_ASSERT(treeIndex != 0);
|
||||
treeIndex--;
|
||||
|
||||
const uint8_t* pData = static_cast<const uint8_t*>(data);
|
||||
for (size_t i = 0; i < pCount; i++, pData += smallerSize) {
|
||||
node = _tree[treeIndex].get(pData);
|
||||
if (node) continue;
|
||||
|
||||
node = ConstPool::Tree::_newNode(_zone, pData, smallerSize, offset + (i * smallerSize), true);
|
||||
_tree[treeIndex].insert(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (_minItemSize == 0)
|
||||
_minItemSize = size;
|
||||
else
|
||||
_minItemSize = Support::min(_minItemSize, size);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ConstPool - Reset
|
||||
// =================
|
||||
|
||||
struct ConstPoolFill {
|
||||
inline ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept :
|
||||
_dst(dst),
|
||||
_dataSize(dataSize) {}
|
||||
|
||||
inline void operator()(const ConstPool::Node* node) noexcept {
|
||||
if (!node->_shared)
|
||||
memcpy(_dst + node->_offset, node->data(), _dataSize);
|
||||
}
|
||||
|
||||
uint8_t* _dst;
|
||||
size_t _dataSize;
|
||||
};
|
||||
|
||||
void ConstPool::fill(void* dst) const noexcept {
|
||||
// Clears possible gaps, asmjit should never emit garbage to the output.
|
||||
memset(dst, 0, _size);
|
||||
|
||||
ConstPoolFill filler(static_cast<uint8_t*>(dst), 1);
|
||||
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
|
||||
_tree[i].forEach(filler);
|
||||
filler._dataSize <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ConstPool - Tests
|
||||
// =================
|
||||
|
||||
#if defined(ASMJIT_TEST)
|
||||
UNIT(const_pool) {
|
||||
Zone zone(32384 - Zone::kBlockOverhead);
|
||||
ConstPool pool(&zone);
|
||||
|
||||
uint32_t i;
|
||||
uint32_t kCount = BrokenAPI::hasArg("--quick") ? 1000 : 1000000;
|
||||
|
||||
INFO("Adding %u constants to the pool", kCount);
|
||||
{
|
||||
size_t prevOffset;
|
||||
size_t curOffset;
|
||||
uint64_t c = 0x0101010101010101u;
|
||||
|
||||
EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk);
|
||||
EXPECT(prevOffset == 0);
|
||||
|
||||
for (i = 1; i < kCount; i++) {
|
||||
c++;
|
||||
EXPECT(pool.add(&c, 8, curOffset) == kErrorOk);
|
||||
EXPECT(prevOffset + 8 == curOffset);
|
||||
EXPECT(pool.size() == (i + 1) * 8);
|
||||
prevOffset = curOffset;
|
||||
}
|
||||
|
||||
EXPECT(pool.alignment() == 8);
|
||||
}
|
||||
|
||||
INFO("Retrieving %u constants from the pool", kCount);
|
||||
{
|
||||
uint64_t c = 0x0101010101010101u;
|
||||
|
||||
for (i = 0; i < kCount; i++) {
|
||||
size_t offset;
|
||||
EXPECT(pool.add(&c, 8, offset) == kErrorOk);
|
||||
EXPECT(offset == i * 8);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
INFO("Checking if the constants were split into 4-byte patterns");
|
||||
{
|
||||
uint32_t c = 0x01010101;
|
||||
for (i = 0; i < kCount; i++) {
|
||||
size_t offset;
|
||||
EXPECT(pool.add(&c, 4, offset) == kErrorOk);
|
||||
EXPECT(offset == i * 8);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
INFO("Adding 2 byte constant to misalign the current offset");
|
||||
{
|
||||
uint16_t c = 0xFFFF;
|
||||
size_t offset;
|
||||
|
||||
EXPECT(pool.add(&c, 2, offset) == kErrorOk);
|
||||
EXPECT(offset == kCount * 8);
|
||||
EXPECT(pool.alignment() == 8);
|
||||
}
|
||||
|
||||
INFO("Adding 8 byte constant to check if pool gets aligned again");
|
||||
{
|
||||
uint64_t c = 0xFFFFFFFFFFFFFFFFu;
|
||||
size_t offset;
|
||||
|
||||
EXPECT(pool.add(&c, 8, offset) == kErrorOk);
|
||||
EXPECT(offset == kCount * 8 + 8);
|
||||
}
|
||||
|
||||
INFO("Adding 2 byte constant to verify the gap is filled");
|
||||
{
|
||||
uint16_t c = 0xFFFE;
|
||||
size_t offset;
|
||||
|
||||
EXPECT(pool.add(&c, 2, offset) == kErrorOk);
|
||||
EXPECT(offset == kCount * 8 + 2);
|
||||
EXPECT(pool.alignment() == 8);
|
||||
}
|
||||
|
||||
INFO("Checking reset functionality");
|
||||
{
|
||||
pool.reset(&zone);
|
||||
zone.reset();
|
||||
|
||||
EXPECT(pool.size() == 0);
|
||||
EXPECT(pool.alignment() == 0);
|
||||
}
|
||||
|
||||
INFO("Checking pool alignment when combined constants are added");
|
||||
{
|
||||
uint8_t bytes[32] = { 0 };
|
||||
size_t offset;
|
||||
|
||||
pool.add(bytes, 1, offset);
|
||||
EXPECT(pool.size() == 1);
|
||||
EXPECT(pool.alignment() == 1);
|
||||
EXPECT(offset == 0);
|
||||
|
||||
pool.add(bytes, 2, offset);
|
||||
EXPECT(pool.size() == 4);
|
||||
EXPECT(pool.alignment() == 2);
|
||||
EXPECT(offset == 2);
|
||||
|
||||
pool.add(bytes, 4, offset);
|
||||
EXPECT(pool.size() == 8);
|
||||
EXPECT(pool.alignment() == 4);
|
||||
EXPECT(offset == 4);
|
||||
|
||||
pool.add(bytes, 4, offset);
|
||||
EXPECT(pool.size() == 8);
|
||||
EXPECT(pool.alignment() == 4);
|
||||
EXPECT(offset == 4);
|
||||
|
||||
pool.add(bytes, 32, offset);
|
||||
EXPECT(pool.size() == 64);
|
||||
EXPECT(pool.alignment() == 32);
|
||||
EXPECT(offset == 32);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
250
lib/lepton/asmjit/core/constpool.h
Normal file
250
lib/lepton/asmjit/core/constpool.h
Normal file
@ -0,0 +1,250 @@
|
||||
// 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_CONSTPOOL_H_INCLUDED
|
||||
#define ASMJIT_CORE_CONSTPOOL_H_INCLUDED
|
||||
|
||||
#include "../core/support.h"
|
||||
#include "../core/zone.h"
|
||||
#include "../core/zonetree.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
|
||||
//! Constant pool scope.
|
||||
enum class ConstPoolScope : uint32_t {
|
||||
//! Local constant, always embedded right after the current function.
|
||||
kLocal = 0,
|
||||
//! Global constant, embedded at the end of the currently compiled code.
|
||||
kGlobal = 1,
|
||||
|
||||
//! Maximum value of `ConstPoolScope`.
|
||||
kMaxValue = kGlobal
|
||||
};
|
||||
|
||||
//! Constant pool.
|
||||
class ConstPool {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(ConstPool)
|
||||
|
||||
//! \cond INTERNAL
|
||||
|
||||
//! Index of a given size in const-pool table.
|
||||
enum Index : uint32_t {
|
||||
kIndex1 = 0,
|
||||
kIndex2 = 1,
|
||||
kIndex4 = 2,
|
||||
kIndex8 = 3,
|
||||
kIndex16 = 4,
|
||||
kIndex32 = 5,
|
||||
kIndex64 = 6,
|
||||
kIndexCount = 7
|
||||
};
|
||||
|
||||
//! Zone-allocated const-pool gap created by two differently aligned constants.
|
||||
struct Gap {
|
||||
//! Pointer to the next gap
|
||||
Gap* _next;
|
||||
//! Offset of the gap.
|
||||
size_t _offset;
|
||||
//! Remaining bytes of the gap (basically a gap size).
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
//! Zone-allocated const-pool node.
|
||||
class Node : public ZoneTreeNodeT<Node> {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(Node)
|
||||
|
||||
//! If this constant is shared with another.
|
||||
uint32_t _shared : 1;
|
||||
//! Data offset from the beginning of the pool.
|
||||
uint32_t _offset;
|
||||
|
||||
inline Node(size_t offset, bool shared) noexcept
|
||||
: ZoneTreeNodeT<Node>(),
|
||||
_shared(shared),
|
||||
_offset(uint32_t(offset)) {}
|
||||
|
||||
inline void* data() const noexcept {
|
||||
return static_cast<void*>(const_cast<ConstPool::Node*>(this) + 1);
|
||||
}
|
||||
};
|
||||
|
||||
//! Data comparer used internally.
|
||||
class Compare {
|
||||
public:
|
||||
size_t _dataSize;
|
||||
|
||||
inline Compare(size_t dataSize) noexcept
|
||||
: _dataSize(dataSize) {}
|
||||
|
||||
inline int operator()(const Node& a, const Node& b) const noexcept {
|
||||
return ::memcmp(a.data(), b.data(), _dataSize);
|
||||
}
|
||||
|
||||
inline int operator()(const Node& a, const void* data) const noexcept {
|
||||
return ::memcmp(a.data(), data, _dataSize);
|
||||
}
|
||||
};
|
||||
|
||||
//! Zone-allocated const-pool tree.
|
||||
struct Tree {
|
||||
//! RB tree.
|
||||
ZoneTree<Node> _tree;
|
||||
//! Size of the tree (number of nodes).
|
||||
size_t _size;
|
||||
//! Size of the data.
|
||||
size_t _dataSize;
|
||||
|
||||
inline explicit Tree(size_t dataSize = 0) noexcept
|
||||
: _tree(),
|
||||
_size(0),
|
||||
_dataSize(dataSize) {}
|
||||
|
||||
inline void reset() noexcept {
|
||||
_tree.reset();
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
inline bool empty() const noexcept { return _size == 0; }
|
||||
inline size_t size() const noexcept { return _size; }
|
||||
|
||||
inline void setDataSize(size_t dataSize) noexcept {
|
||||
ASMJIT_ASSERT(empty());
|
||||
_dataSize = dataSize;
|
||||
}
|
||||
|
||||
inline Node* get(const void* data) noexcept {
|
||||
Compare cmp(_dataSize);
|
||||
return _tree.get(data, cmp);
|
||||
}
|
||||
|
||||
inline void insert(Node* node) noexcept {
|
||||
Compare cmp(_dataSize);
|
||||
_tree.insert(node, cmp);
|
||||
_size++;
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
inline void forEach(Visitor& visitor) const noexcept {
|
||||
Node* node = _tree.root();
|
||||
if (!node) return;
|
||||
|
||||
Node* stack[Globals::kMaxTreeHeight];
|
||||
size_t top = 0;
|
||||
|
||||
for (;;) {
|
||||
Node* left = node->left();
|
||||
if (left != nullptr) {
|
||||
ASMJIT_ASSERT(top != Globals::kMaxTreeHeight);
|
||||
stack[top++] = node;
|
||||
|
||||
node = left;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
visitor(node);
|
||||
node = node->right();
|
||||
|
||||
if (node != nullptr)
|
||||
break;
|
||||
|
||||
if (top == 0)
|
||||
return;
|
||||
|
||||
node = stack[--top];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept {
|
||||
Node* node = zone->allocT<Node>(sizeof(Node) + size);
|
||||
if (ASMJIT_UNLIKELY(!node)) return nullptr;
|
||||
|
||||
node = new(node) Node(offset, shared);
|
||||
memcpy(node->data(), data, size);
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
//! \endcond
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Zone allocator.
|
||||
Zone* _zone;
|
||||
//! Tree per size.
|
||||
Tree _tree[kIndexCount];
|
||||
//! Gaps per size.
|
||||
Gap* _gaps[kIndexCount];
|
||||
//! Gaps pool
|
||||
Gap* _gapPool;
|
||||
|
||||
//! Size of the pool (in bytes).
|
||||
size_t _size;
|
||||
//! Required pool alignment.
|
||||
size_t _alignment;
|
||||
//! Minimum item size in the pool.
|
||||
size_t _minItemSize;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API ConstPool(Zone* zone) noexcept;
|
||||
ASMJIT_API ~ConstPool() noexcept;
|
||||
|
||||
ASMJIT_API void reset(Zone* zone) noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Tests whether the constant-pool is empty.
|
||||
inline bool empty() const noexcept { return _size == 0; }
|
||||
//! Returns the size of the constant-pool in bytes.
|
||||
inline size_t size() const noexcept { return _size; }
|
||||
//! Returns minimum alignment.
|
||||
inline size_t alignment() const noexcept { return _alignment; }
|
||||
//! Returns the minimum size of all items added to the constant pool.
|
||||
inline size_t minItemSize() const noexcept { return _minItemSize; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Utilities
|
||||
//! \{
|
||||
|
||||
//! Adds a constant to the constant pool.
|
||||
//!
|
||||
//! The constant must have known size, which is 1, 2, 4, 8, 16 or 32 bytes. The constant is added to the pool only
|
||||
//! if it doesn't not exist, otherwise cached value is returned.
|
||||
//!
|
||||
//! AsmJit is able to subdivide added constants, so for example if you add 8-byte constant 0x1122334455667788 it
|
||||
//! will create the following slots:
|
||||
//!
|
||||
//! 8-byte: 0x1122334455667788
|
||||
//! 4-byte: 0x11223344, 0x55667788
|
||||
//!
|
||||
//! The reason is that when combining MMX/SSE/AVX code some patterns are used frequently. However, AsmJit is not
|
||||
//! able to reallocate a constant that has been already added. For example if you try to add 4-byte constant and
|
||||
//! then 8-byte constant having the same 4-byte pattern as the previous one, two independent slots will be used.
|
||||
ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset) noexcept;
|
||||
|
||||
//! Fills the destination with the content of this constant pool.
|
||||
ASMJIT_API void fill(void* dst) const noexcept;
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_CONSTPOOL_H_INCLUDED
|
||||
1162
lib/lepton/asmjit/core/cpuinfo.cpp
Normal file
1162
lib/lepton/asmjit/core/cpuinfo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
813
lib/lepton/asmjit/core/cpuinfo.h
Normal file
813
lib/lepton/asmjit/core/cpuinfo.h
Normal file
@ -0,0 +1,813 @@
|
||||
// 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_CPUINFO_H_INCLUDED
|
||||
#define ASMJIT_CORE_CPUINFO_H_INCLUDED
|
||||
|
||||
#include "../core/archtraits.h"
|
||||
#include "../core/environment.h"
|
||||
#include "../core/globals.h"
|
||||
#include "../core/string.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
//! CPU features information.
|
||||
//!
|
||||
//! Each feature is represented by a single bit in an embedded bit array.
|
||||
class CpuFeatures {
|
||||
public:
|
||||
//! A word that is used to represents feature bits.
|
||||
typedef Support::BitWord BitWord;
|
||||
//! Iterator that can iterate all CPU features set.
|
||||
typedef Support::BitVectorIterator<BitWord> Iterator;
|
||||
|
||||
//! \name Constants
|
||||
//! \{
|
||||
|
||||
//! \cond INTERNAL
|
||||
enum : uint32_t {
|
||||
kMaxFeatures = 256,
|
||||
kNumBitWords = kMaxFeatures / Support::kBitWordSizeInBits
|
||||
};
|
||||
//! \endcond
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Data
|
||||
//! \{
|
||||
|
||||
//! CPU features data.
|
||||
struct Data {
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Data bits.
|
||||
Support::Array<BitWord, kNumBitWords> _bits;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline bool operator==(const Data& other) noexcept { return eq(other); }
|
||||
inline bool operator!=(const Data& other) noexcept { return !eq(other); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns true if there are no features set.
|
||||
inline bool empty() const noexcept { return _bits.aggregate<Support::Or>(0) == 0; }
|
||||
|
||||
//! Returns all features as array of bitwords (see \ref Support::BitWord).
|
||||
inline BitWord* bits() noexcept { return _bits.data(); }
|
||||
//! Returns all features as array of bitwords (const).
|
||||
inline const BitWord* bits() const noexcept { return _bits.data(); }
|
||||
|
||||
//! Returns the number of BitWords returned by \ref bits().
|
||||
inline size_t bitWordCount() const noexcept { return kNumBitWords; }
|
||||
|
||||
//! Returns \ref Support::BitVectorIterator, that can be used to iterate over all features efficiently.
|
||||
inline Iterator iterator() const noexcept { return Iterator(_bits.data(), kNumBitWords); }
|
||||
|
||||
//! Tests whether the feature `featureId` is present.
|
||||
template<typename FeatureId>
|
||||
ASMJIT_FORCE_INLINE bool has(const FeatureId& featureId) const noexcept {
|
||||
ASMJIT_ASSERT(uint32_t(featureId) < kMaxFeatures);
|
||||
|
||||
uint32_t idx = uint32_t(featureId) / Support::kBitWordSizeInBits;
|
||||
uint32_t bit = uint32_t(featureId) % Support::kBitWordSizeInBits;
|
||||
|
||||
return bool((_bits[idx] >> bit) & 0x1);
|
||||
}
|
||||
|
||||
//! Tests whether all features as defined by `other` are present.
|
||||
ASMJIT_FORCE_INLINE bool hasAll(const Data& other) const noexcept {
|
||||
for (uint32_t i = 0; i < kNumBitWords; i++)
|
||||
if ((_bits[i] & other._bits[i]) != other._bits[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Manipulation
|
||||
//! \{
|
||||
|
||||
inline void reset() noexcept { _bits.fill(0); }
|
||||
|
||||
//! Adds the given CPU `featureId` to the list of features.
|
||||
template<typename FeatureId>
|
||||
ASMJIT_FORCE_INLINE void add(const FeatureId& featureId) noexcept {
|
||||
ASMJIT_ASSERT(uint32_t(featureId) < kMaxFeatures);
|
||||
|
||||
uint32_t idx = uint32_t(featureId) / Support::kBitWordSizeInBits;
|
||||
uint32_t bit = uint32_t(featureId) % Support::kBitWordSizeInBits;
|
||||
|
||||
_bits[idx] |= BitWord(1) << bit;
|
||||
}
|
||||
|
||||
template<typename FeatureId, typename... Args>
|
||||
ASMJIT_FORCE_INLINE void add(const FeatureId& featureId, Args&&... otherFeatureIds) noexcept {
|
||||
add(featureId);
|
||||
add(std::forward<Args>(otherFeatureIds)...);
|
||||
}
|
||||
|
||||
template<typename FeatureId>
|
||||
ASMJIT_FORCE_INLINE void addIf(bool condition, const FeatureId& featureId) noexcept {
|
||||
ASMJIT_ASSERT(uint32_t(featureId) < kMaxFeatures);
|
||||
|
||||
uint32_t idx = uint32_t(featureId) / Support::kBitWordSizeInBits;
|
||||
uint32_t bit = uint32_t(featureId) % Support::kBitWordSizeInBits;
|
||||
|
||||
_bits[idx] |= BitWord(condition) << bit;
|
||||
}
|
||||
|
||||
template<typename FeatureId, typename... Args>
|
||||
ASMJIT_FORCE_INLINE void addIf(bool condition, const FeatureId& featureId, Args&&... otherFeatureIds) noexcept {
|
||||
addIf(condition, featureId);
|
||||
addIf(condition, std::forward<Args>(otherFeatureIds)...);
|
||||
}
|
||||
|
||||
//! Removes the given CPU `featureId` from the list of features.
|
||||
template<typename FeatureId>
|
||||
ASMJIT_FORCE_INLINE void remove(const FeatureId& featureId) noexcept {
|
||||
ASMJIT_ASSERT(uint32_t(featureId) < kMaxFeatures);
|
||||
|
||||
uint32_t idx = uint32_t(featureId) / Support::kBitWordSizeInBits;
|
||||
uint32_t bit = uint32_t(featureId) % Support::kBitWordSizeInBits;
|
||||
|
||||
_bits[idx] &= ~(BitWord(1) << bit);
|
||||
}
|
||||
|
||||
template<typename FeatureId, typename... Args>
|
||||
ASMJIT_FORCE_INLINE void remove(const FeatureId& featureId, Args&&... otherFeatureIds) noexcept {
|
||||
remove(featureId);
|
||||
remove(std::forward<Args>(otherFeatureIds)...);
|
||||
}
|
||||
|
||||
//! Tests whether this CPU features data matches `other`.
|
||||
ASMJIT_FORCE_INLINE bool eq(const Data& other) const noexcept { return _bits == other._bits; }
|
||||
|
||||
//! \}
|
||||
|
||||
};
|
||||
|
||||
//! X86 specific features data.
|
||||
struct X86 : public Data {
|
||||
//! X86 CPU feature identifiers.
|
||||
enum Id : uint8_t {
|
||||
// @EnumValuesBegin{"enum": "CpuFeatures::X86"}@
|
||||
kNone, //!< No feature (never set, used internally).
|
||||
|
||||
kMT, //!< CPU has multi-threading capabilities.
|
||||
kNX, //!< CPU has Not-Execute-Bit aka DEP (data-execution prevention).
|
||||
k3DNOW, //!< CPU has 3DNOW (3DNOW base instructions) [AMD].
|
||||
k3DNOW2, //!< CPU has 3DNOW2 (enhanced 3DNOW) [AMD].
|
||||
kADX, //!< CPU has ADX (multi-precision add-carry instruction extensions).
|
||||
kAESNI, //!< CPU has AESNI (AES encode/decode instructions).
|
||||
kALTMOVCR8, //!< CPU has LOCK MOV R<->CR0 (supports `MOV R<->CR8` via `LOCK MOV R<->CR0` in 32-bit mode) [AMD].
|
||||
kAMX_BF16, //!< CPU has AMX_BF16 (advanced matrix extensions - BF16 instructions).
|
||||
kAMX_INT8, //!< CPU has AMX_INT8 (advanced matrix extensions - INT8 instructions).
|
||||
kAMX_TILE, //!< CPU has AMX_TILE (advanced matrix extensions).
|
||||
kAVX, //!< CPU has AVX (advanced vector extensions).
|
||||
kAVX2, //!< CPU has AVX2 (advanced vector extensions 2).
|
||||
kAVX512_4FMAPS, //!< CPU has AVX512_FMAPS (FMA packed single).
|
||||
kAVX512_4VNNIW, //!< CPU has AVX512_VNNIW (vector NN instructions word variable precision).
|
||||
kAVX512_BF16, //!< CPU has AVX512_BF16 (BFLOAT16 support instruction).
|
||||
kAVX512_BITALG, //!< CPU has AVX512_BITALG (VPOPCNT[B|W], VPSHUFBITQMB).
|
||||
kAVX512_BW, //!< CPU has AVX512_BW (packed BYTE|WORD).
|
||||
kAVX512_CDI, //!< CPU has AVX512_CDI (conflict detection).
|
||||
kAVX512_DQ, //!< CPU has AVX512_DQ (packed DWORD|QWORD).
|
||||
kAVX512_ERI, //!< CPU has AVX512_ERI (exponential and reciprocal).
|
||||
kAVX512_F, //!< CPU has AVX512_F (AVX512 foundation).
|
||||
kAVX512_FP16, //!< CPU has AVX512_FP16 (FP16 extensions).
|
||||
kAVX512_IFMA, //!< CPU has AVX512_IFMA (integer fused-multiply-add using 52-bit precision).
|
||||
kAVX512_PFI, //!< CPU has AVX512_PFI (prefetch instructions).
|
||||
kAVX512_VBMI, //!< CPU has AVX512_VBMI (vector byte manipulation).
|
||||
kAVX512_VBMI2, //!< CPU has AVX512_VBMI2 (vector byte manipulation 2).
|
||||
kAVX512_VL, //!< CPU has AVX512_VL (vector length extensions).
|
||||
kAVX512_VNNI, //!< CPU has AVX512_VNNI (vector neural network instructions).
|
||||
kAVX512_VP2INTERSECT, //!< CPU has AVX512_VP2INTERSECT
|
||||
kAVX512_VPOPCNTDQ, //!< CPU has AVX512_VPOPCNTDQ (VPOPCNT[D|Q] instructions).
|
||||
kAVX_VNNI, //!< CPU has AVX_VNNI (VEX encoding of vpdpbusd/vpdpbusds/vpdpwssd/vpdpwssds).
|
||||
kBMI, //!< CPU has BMI (bit manipulation instructions #1).
|
||||
kBMI2, //!< CPU has BMI2 (bit manipulation instructions #2).
|
||||
kCET_IBT, //!< CPU has CET-IBT (indirect branch tracking).
|
||||
kCET_SS, //!< CPU has CET-SS.
|
||||
kCLDEMOTE, //!< CPU has CLDEMOTE (cache line demote).
|
||||
kCLFLUSH, //!< CPU has CLFUSH (Cache Line flush).
|
||||
kCLFLUSHOPT, //!< CPU has CLFUSHOPT (Cache Line flush - optimized).
|
||||
kCLWB, //!< CPU has CLWB.
|
||||
kCLZERO, //!< CPU has CLZERO.
|
||||
kCMOV, //!< CPU has CMOV (CMOV and FCMOV instructions).
|
||||
kCMPXCHG16B, //!< CPU has CMPXCHG16B (compare-exchange 16 bytes) [X86_64].
|
||||
kCMPXCHG8B, //!< CPU has CMPXCHG8B (compare-exchange 8 bytes).
|
||||
kENCLV, //!< CPU has ENCLV.
|
||||
kENQCMD, //!< CPU has ENQCMD (enqueue stores).
|
||||
kERMS, //!< CPU has ERMS (enhanced REP MOVSB/STOSB).
|
||||
kF16C, //!< CPU has F16C.
|
||||
kFMA, //!< CPU has FMA (fused-multiply-add 3 operand form).
|
||||
kFMA4, //!< CPU has FMA4 (fused-multiply-add 4 operand form).
|
||||
kFPU, //!< CPU has FPU (FPU support).
|
||||
kFSGSBASE, //!< CPU has FSGSBASE.
|
||||
kFXSR, //!< CPU has FXSR (FXSAVE/FXRSTOR instructions).
|
||||
kFXSROPT, //!< CPU has FXSROTP (FXSAVE/FXRSTOR is optimized).
|
||||
kGEODE, //!< CPU has GEODE extensions (3DNOW additions).
|
||||
kGFNI, //!< CPU has GFNI (Galois field instructions).
|
||||
kHLE, //!< CPU has HLE.
|
||||
kHRESET, //!< CPU has HRESET.
|
||||
kI486, //!< CPU has I486 features (I486+ support).
|
||||
kLAHFSAHF, //!< CPU has LAHF/SAHF (LAHF/SAHF in 64-bit mode) [X86_64].
|
||||
kLWP, //!< CPU has LWP (lightweight profiling) [AMD].
|
||||
kLZCNT, //!< CPU has LZCNT (LZCNT instruction).
|
||||
kMCOMMIT, //!< CPU has MCOMMIT (MCOMMIT instruction).
|
||||
kMMX, //!< CPU has MMX (MMX base instructions).
|
||||
kMMX2, //!< CPU has MMX2 (MMX extensions or MMX2).
|
||||
kMONITOR, //!< CPU has MONITOR (MONITOR/MWAIT instructions).
|
||||
kMONITORX, //!< CPU has MONITORX (MONITORX/MWAITX instructions).
|
||||
kMOVBE, //!< CPU has MOVBE (move with byte-order swap).
|
||||
kMOVDIR64B, //!< CPU has MOVDIR64B (move 64 bytes as direct store).
|
||||
kMOVDIRI, //!< CPU has MOVDIRI (move dword/qword as direct store).
|
||||
kMPX, //!< CPU has MPX (memory protection extensions).
|
||||
kMSR, //!< CPU has MSR (RDMSR/WRMSR instructions).
|
||||
kMSSE, //!< CPU has MSSE (misaligned SSE support).
|
||||
kOSXSAVE, //!< CPU has OSXSAVE (XSAVE enabled by OS).
|
||||
kOSPKE, //!< CPU has OSPKE (PKE enabled by OS).
|
||||
kPCLMULQDQ, //!< CPU has PCLMULQDQ (packed carry-less multiplication).
|
||||
kPCONFIG, //!< CPU has PCONFIG (PCONFIG instruction).
|
||||
kPOPCNT, //!< CPU has POPCNT (POPCNT instruction).
|
||||
kPREFETCHW, //!< CPU has PREFETCHW.
|
||||
kPREFETCHWT1, //!< CPU has PREFETCHWT1.
|
||||
kPTWRITE, //!< CPU has PTWRITE.
|
||||
kRDPID, //!< CPU has RDPID.
|
||||
kRDPRU, //!< CPU has RDPRU.
|
||||
kRDRAND, //!< CPU has RDRAND.
|
||||
kRDSEED, //!< CPU has RDSEED.
|
||||
kRDTSC, //!< CPU has RDTSC.
|
||||
kRDTSCP, //!< CPU has RDTSCP.
|
||||
kRTM, //!< CPU has RTM.
|
||||
kSERIALIZE, //!< CPU has SERIALIZE.
|
||||
kSHA, //!< CPU has SHA (SHA-1 and SHA-256 instructions).
|
||||
kSKINIT, //!< CPU has SKINIT (SKINIT/STGI instructions) [AMD].
|
||||
kSMAP, //!< CPU has SMAP (supervisor-mode access prevention).
|
||||
kSMEP, //!< CPU has SMEP (supervisor-mode execution prevention).
|
||||
kSMX, //!< CPU has SMX (safer mode extensions).
|
||||
kSNP, //!< CPU has SNP.
|
||||
kSSE, //!< CPU has SSE.
|
||||
kSSE2, //!< CPU has SSE2.
|
||||
kSSE3, //!< CPU has SSE3.
|
||||
kSSE4_1, //!< CPU has SSE4.1.
|
||||
kSSE4_2, //!< CPU has SSE4.2.
|
||||
kSSE4A, //!< CPU has SSE4A [AMD].
|
||||
kSSSE3, //!< CPU has SSSE3.
|
||||
kSVM, //!< CPU has SVM (virtualization) [AMD].
|
||||
kTBM, //!< CPU has TBM (trailing bit manipulation) [AMD].
|
||||
kTSX, //!< CPU has TSX.
|
||||
kTSXLDTRK, //!< CPU has TSXLDTRK.
|
||||
kUINTR, //!< CPU has UINTR (user interrupts).
|
||||
kVAES, //!< CPU has VAES (vector AES 256|512 bit support).
|
||||
kVMX, //!< CPU has VMX (virtualization) [INTEL].
|
||||
kVPCLMULQDQ, //!< CPU has VPCLMULQDQ (vector PCLMULQDQ 256|512-bit support).
|
||||
kWAITPKG, //!< CPU has WAITPKG (UMONITOR, UMWAIT, TPAUSE).
|
||||
kWBNOINVD, //!< CPU has WBNOINVD.
|
||||
kXOP, //!< CPU has XOP (XOP instructions) [AMD].
|
||||
kXSAVE, //!< CPU has XSAVE.
|
||||
kXSAVEC, //!< CPU has XSAVEC.
|
||||
kXSAVEOPT, //!< CPU has XSAVEOPT.
|
||||
kXSAVES, //!< CPU has XSAVES.
|
||||
// @EnumValuesEnd@
|
||||
|
||||
kMaxValue = kXSAVES
|
||||
};
|
||||
|
||||
#define ASMJIT_X86_FEATURE(FEATURE) \
|
||||
inline bool has##FEATURE() const noexcept { return has(X86::k##FEATURE); }
|
||||
|
||||
ASMJIT_X86_FEATURE(MT)
|
||||
ASMJIT_X86_FEATURE(NX)
|
||||
ASMJIT_X86_FEATURE(3DNOW)
|
||||
ASMJIT_X86_FEATURE(3DNOW2)
|
||||
ASMJIT_X86_FEATURE(ADX)
|
||||
ASMJIT_X86_FEATURE(AESNI)
|
||||
ASMJIT_X86_FEATURE(ALTMOVCR8)
|
||||
ASMJIT_X86_FEATURE(AMX_BF16)
|
||||
ASMJIT_X86_FEATURE(AMX_INT8)
|
||||
ASMJIT_X86_FEATURE(AMX_TILE)
|
||||
ASMJIT_X86_FEATURE(AVX)
|
||||
ASMJIT_X86_FEATURE(AVX2)
|
||||
ASMJIT_X86_FEATURE(AVX512_4FMAPS)
|
||||
ASMJIT_X86_FEATURE(AVX512_4VNNIW)
|
||||
ASMJIT_X86_FEATURE(AVX512_BF16)
|
||||
ASMJIT_X86_FEATURE(AVX512_BITALG)
|
||||
ASMJIT_X86_FEATURE(AVX512_BW)
|
||||
ASMJIT_X86_FEATURE(AVX512_CDI)
|
||||
ASMJIT_X86_FEATURE(AVX512_DQ)
|
||||
ASMJIT_X86_FEATURE(AVX512_ERI)
|
||||
ASMJIT_X86_FEATURE(AVX512_F)
|
||||
ASMJIT_X86_FEATURE(AVX512_FP16)
|
||||
ASMJIT_X86_FEATURE(AVX512_IFMA)
|
||||
ASMJIT_X86_FEATURE(AVX512_PFI)
|
||||
ASMJIT_X86_FEATURE(AVX512_VBMI)
|
||||
ASMJIT_X86_FEATURE(AVX512_VBMI2)
|
||||
ASMJIT_X86_FEATURE(AVX512_VL)
|
||||
ASMJIT_X86_FEATURE(AVX512_VNNI)
|
||||
ASMJIT_X86_FEATURE(AVX512_VP2INTERSECT)
|
||||
ASMJIT_X86_FEATURE(AVX512_VPOPCNTDQ)
|
||||
ASMJIT_X86_FEATURE(AVX_VNNI)
|
||||
ASMJIT_X86_FEATURE(BMI)
|
||||
ASMJIT_X86_FEATURE(BMI2)
|
||||
ASMJIT_X86_FEATURE(CET_IBT)
|
||||
ASMJIT_X86_FEATURE(CET_SS)
|
||||
ASMJIT_X86_FEATURE(CLDEMOTE)
|
||||
ASMJIT_X86_FEATURE(CLFLUSH)
|
||||
ASMJIT_X86_FEATURE(CLFLUSHOPT)
|
||||
ASMJIT_X86_FEATURE(CLWB)
|
||||
ASMJIT_X86_FEATURE(CLZERO)
|
||||
ASMJIT_X86_FEATURE(CMOV)
|
||||
ASMJIT_X86_FEATURE(CMPXCHG16B)
|
||||
ASMJIT_X86_FEATURE(CMPXCHG8B)
|
||||
ASMJIT_X86_FEATURE(ENCLV)
|
||||
ASMJIT_X86_FEATURE(ENQCMD)
|
||||
ASMJIT_X86_FEATURE(ERMS)
|
||||
ASMJIT_X86_FEATURE(F16C)
|
||||
ASMJIT_X86_FEATURE(FMA)
|
||||
ASMJIT_X86_FEATURE(FMA4)
|
||||
ASMJIT_X86_FEATURE(FPU)
|
||||
ASMJIT_X86_FEATURE(FSGSBASE)
|
||||
ASMJIT_X86_FEATURE(FXSR)
|
||||
ASMJIT_X86_FEATURE(FXSROPT)
|
||||
ASMJIT_X86_FEATURE(GEODE)
|
||||
ASMJIT_X86_FEATURE(GFNI)
|
||||
ASMJIT_X86_FEATURE(HLE)
|
||||
ASMJIT_X86_FEATURE(HRESET)
|
||||
ASMJIT_X86_FEATURE(I486)
|
||||
ASMJIT_X86_FEATURE(LAHFSAHF)
|
||||
ASMJIT_X86_FEATURE(LWP)
|
||||
ASMJIT_X86_FEATURE(LZCNT)
|
||||
ASMJIT_X86_FEATURE(MCOMMIT)
|
||||
ASMJIT_X86_FEATURE(MMX)
|
||||
ASMJIT_X86_FEATURE(MMX2)
|
||||
ASMJIT_X86_FEATURE(MONITOR)
|
||||
ASMJIT_X86_FEATURE(MONITORX)
|
||||
ASMJIT_X86_FEATURE(MOVBE)
|
||||
ASMJIT_X86_FEATURE(MOVDIR64B)
|
||||
ASMJIT_X86_FEATURE(MOVDIRI)
|
||||
ASMJIT_X86_FEATURE(MPX)
|
||||
ASMJIT_X86_FEATURE(MSR)
|
||||
ASMJIT_X86_FEATURE(MSSE)
|
||||
ASMJIT_X86_FEATURE(OSXSAVE)
|
||||
ASMJIT_X86_FEATURE(OSPKE)
|
||||
ASMJIT_X86_FEATURE(PCLMULQDQ)
|
||||
ASMJIT_X86_FEATURE(PCONFIG)
|
||||
ASMJIT_X86_FEATURE(POPCNT)
|
||||
ASMJIT_X86_FEATURE(PREFETCHW)
|
||||
ASMJIT_X86_FEATURE(PREFETCHWT1)
|
||||
ASMJIT_X86_FEATURE(PTWRITE)
|
||||
ASMJIT_X86_FEATURE(RDPID)
|
||||
ASMJIT_X86_FEATURE(RDPRU)
|
||||
ASMJIT_X86_FEATURE(RDRAND)
|
||||
ASMJIT_X86_FEATURE(RDSEED)
|
||||
ASMJIT_X86_FEATURE(RDTSC)
|
||||
ASMJIT_X86_FEATURE(RDTSCP)
|
||||
ASMJIT_X86_FEATURE(RTM)
|
||||
ASMJIT_X86_FEATURE(SERIALIZE)
|
||||
ASMJIT_X86_FEATURE(SHA)
|
||||
ASMJIT_X86_FEATURE(SKINIT)
|
||||
ASMJIT_X86_FEATURE(SMAP)
|
||||
ASMJIT_X86_FEATURE(SMEP)
|
||||
ASMJIT_X86_FEATURE(SMX)
|
||||
ASMJIT_X86_FEATURE(SNP)
|
||||
ASMJIT_X86_FEATURE(SSE)
|
||||
ASMJIT_X86_FEATURE(SSE2)
|
||||
ASMJIT_X86_FEATURE(SSE3)
|
||||
ASMJIT_X86_FEATURE(SSE4_1)
|
||||
ASMJIT_X86_FEATURE(SSE4_2)
|
||||
ASMJIT_X86_FEATURE(SSE4A)
|
||||
ASMJIT_X86_FEATURE(SSSE3)
|
||||
ASMJIT_X86_FEATURE(SVM)
|
||||
ASMJIT_X86_FEATURE(TBM)
|
||||
ASMJIT_X86_FEATURE(TSX)
|
||||
ASMJIT_X86_FEATURE(TSXLDTRK)
|
||||
ASMJIT_X86_FEATURE(UINTR)
|
||||
ASMJIT_X86_FEATURE(VAES)
|
||||
ASMJIT_X86_FEATURE(VMX)
|
||||
ASMJIT_X86_FEATURE(VPCLMULQDQ)
|
||||
ASMJIT_X86_FEATURE(WAITPKG)
|
||||
ASMJIT_X86_FEATURE(WBNOINVD)
|
||||
ASMJIT_X86_FEATURE(XOP)
|
||||
ASMJIT_X86_FEATURE(XSAVE)
|
||||
ASMJIT_X86_FEATURE(XSAVEC)
|
||||
ASMJIT_X86_FEATURE(XSAVEOPT)
|
||||
ASMJIT_X86_FEATURE(XSAVES)
|
||||
|
||||
#undef ASMJIT_X86_FEATURE
|
||||
};
|
||||
|
||||
//! ARM specific features data.
|
||||
struct ARM : public Data {
|
||||
//! ARM CPU feature identifiers.
|
||||
enum Id : uint8_t {
|
||||
// @EnumValuesBegin{"enum": "CpuFeatures::ARM"}@
|
||||
kNone = 0, //!< No feature (never set, used internally).
|
||||
kTHUMB, //!< THUMB v1 ISA.
|
||||
kTHUMBv2, //!< THUMB v2 ISA.
|
||||
|
||||
kARMv6, //!< ARMv6 ISA.
|
||||
kARMv7, //!< ARMv7 ISA.
|
||||
kARMv8a, //!< ARMv8-A ISA.
|
||||
kARMv8_1a, //!< ARMv8.1-A ISA.
|
||||
kARMv8_2a, //!< ARMv8.2-A ISA.
|
||||
kARMv8_3a, //!< ARMv8.3-A ISA.
|
||||
kARMv8_4a, //!< ARMv8.4-A ISA.
|
||||
kARMv8_5a, //!< ARMv8.5-A ISA.
|
||||
kARMv8_6a, //!< ARMv8.6-A ISA.
|
||||
kARMv8_7a, //!< ARMv8.7-A ISA.
|
||||
|
||||
kVFPv2, //!< CPU has VFPv2 instruction set.
|
||||
kVFPv3, //!< CPU has VFPv3 instruction set.
|
||||
kVFPv4, //!< CPU has VFPv4 instruction set.
|
||||
kVFP_D32, //!< CPU has 32 VFP-D (64-bit) registers.
|
||||
|
||||
kAES, //!< CPU has AES (AArch64 only).
|
||||
kALTNZCV, //!< CPU has ALTNZCV (AArch64 only).
|
||||
kASIMD, //!< CPU has Advanced SIMD (NEON on ARM/THUMB).
|
||||
kBF16, //!< CPU has BF16 (AArch64 only).
|
||||
kBTI, //!< CPU has BTI (branch target identification).
|
||||
kCPUID, //!< CPU has accessible CPUID register (ID_AA64ZFR0_EL1).
|
||||
kCRC32, //!< CPU has CRC32 .
|
||||
kDGH, //!< CPU has DGH (AArch64 only).
|
||||
kDIT, //!< CPU has data independent timing instructions (DIT).
|
||||
kDOTPROD, //!< CPU has DOTPROD (SDOT/UDOT).
|
||||
kEDSP, //!< CPU has EDSP (ARM/THUMB only).
|
||||
kFCMA, //!< CPU has FCMA (FCADD/FCMLA).
|
||||
kFJCVTZS, //!< CPU has FJCVTZS (AArch64 only).
|
||||
kFLAGM, //!< CPU has FLAGM (AArch64 only).
|
||||
kFP16CONV, //!< CPU has FP16 (half-float) conversion.
|
||||
kFP16FML, //!< CPU has FMLAL{2}/FMLSL{2}
|
||||
kFP16FULL, //!< CPU has full support for FP16.
|
||||
kFRINT, //!< CPU has FRINT[32|64][X|Z] (AArch64 only).
|
||||
kI8MM, //!< CPU has I8MM (AArch64 only).
|
||||
kIDIVA, //!< CPU has hardware SDIV and UDIV (ARM mode).
|
||||
kIDIVT, //!< CPU has hardware SDIV and UDIV (THUMB mode).
|
||||
kLSE, //!< CPU has large system extensions (LSE) (AArch64 only).
|
||||
kMTE, //!< CPU has MTE (AArch64 only).
|
||||
kRCPC_IMMO, //!< CPU has RCPC_IMMO (AArch64 only).
|
||||
kRDM, //!< CPU has RDM (AArch64 only).
|
||||
kPMU, //!< CPU has PMU (AArch64 only).
|
||||
kPMULL, //!< CPU has PMULL (AArch64 only).
|
||||
kRNG, //!< CPU has random number generation (RNG).
|
||||
kSB, //!< CPU has speculative barrier SB (AArch64 only).
|
||||
kSHA1, //!< CPU has SHA1.
|
||||
kSHA2, //!< CPU has SHA2.
|
||||
kSHA3, //!< CPU has SHA3.
|
||||
kSHA512, //!< CPU has SHA512.
|
||||
kSM3, //!< CPU has SM3.
|
||||
kSM4, //!< CPU has SM4.
|
||||
kSSBS, //!< CPU has SSBS.
|
||||
kSVE, //!< CPU has SVE (AArch64 only).
|
||||
kSVE_BF16, //!< CPU has SVE-BF16 (AArch64 only).
|
||||
kSVE_F32MM, //!< CPU has SVE-F32MM (AArch64 only).
|
||||
kSVE_F64MM, //!< CPU has SVE-F64MM (AArch64 only).
|
||||
kSVE_I8MM, //!< CPU has SVE-I8MM (AArch64 only).
|
||||
kSVE_PMULL, //!< CPU has SVE-PMULL (AArch64 only).
|
||||
kSVE2, //!< CPU has SVE2 (AArch64 only).
|
||||
kSVE2_AES, //!< CPU has SVE2-AES (AArch64 only).
|
||||
kSVE2_BITPERM, //!< CPU has SVE2-BITPERM (AArch64 only).
|
||||
kSVE2_SHA3, //!< CPU has SVE2-SHA3 (AArch64 only).
|
||||
kSVE2_SM4, //!< CPU has SVE2-SM4 (AArch64 only).
|
||||
kTME, //!< CPU has transactional memory extensions (TME).
|
||||
// @EnumValuesEnd@
|
||||
|
||||
kMaxValue = kTME
|
||||
};
|
||||
|
||||
#define ASMJIT_ARM_FEATURE(FEATURE) \
|
||||
inline bool has##FEATURE() const noexcept { return has(ARM::k##FEATURE); }
|
||||
|
||||
ASMJIT_ARM_FEATURE(THUMB)
|
||||
ASMJIT_ARM_FEATURE(THUMBv2)
|
||||
|
||||
ASMJIT_ARM_FEATURE(ARMv6)
|
||||
ASMJIT_ARM_FEATURE(ARMv7)
|
||||
ASMJIT_ARM_FEATURE(ARMv8a)
|
||||
ASMJIT_ARM_FEATURE(ARMv8_1a)
|
||||
ASMJIT_ARM_FEATURE(ARMv8_2a)
|
||||
ASMJIT_ARM_FEATURE(ARMv8_3a)
|
||||
ASMJIT_ARM_FEATURE(ARMv8_4a)
|
||||
ASMJIT_ARM_FEATURE(ARMv8_5a)
|
||||
ASMJIT_ARM_FEATURE(ARMv8_6a)
|
||||
ASMJIT_ARM_FEATURE(ARMv8_7a)
|
||||
|
||||
ASMJIT_ARM_FEATURE(VFPv2)
|
||||
ASMJIT_ARM_FEATURE(VFPv3)
|
||||
ASMJIT_ARM_FEATURE(VFPv4)
|
||||
ASMJIT_ARM_FEATURE(VFP_D32)
|
||||
|
||||
ASMJIT_ARM_FEATURE(AES)
|
||||
ASMJIT_ARM_FEATURE(ALTNZCV)
|
||||
ASMJIT_ARM_FEATURE(ASIMD)
|
||||
ASMJIT_ARM_FEATURE(BF16)
|
||||
ASMJIT_ARM_FEATURE(BTI)
|
||||
ASMJIT_ARM_FEATURE(CPUID)
|
||||
ASMJIT_ARM_FEATURE(CRC32)
|
||||
ASMJIT_ARM_FEATURE(DGH)
|
||||
ASMJIT_ARM_FEATURE(DIT)
|
||||
ASMJIT_ARM_FEATURE(DOTPROD)
|
||||
ASMJIT_ARM_FEATURE(EDSP)
|
||||
ASMJIT_ARM_FEATURE(FCMA)
|
||||
ASMJIT_ARM_FEATURE(FLAGM)
|
||||
ASMJIT_ARM_FEATURE(FP16CONV)
|
||||
ASMJIT_ARM_FEATURE(FP16FML)
|
||||
ASMJIT_ARM_FEATURE(FP16FULL)
|
||||
ASMJIT_ARM_FEATURE(FRINT)
|
||||
ASMJIT_ARM_FEATURE(IDIVA)
|
||||
ASMJIT_ARM_FEATURE(IDIVT)
|
||||
ASMJIT_ARM_FEATURE(LSE)
|
||||
ASMJIT_ARM_FEATURE(MTE)
|
||||
ASMJIT_ARM_FEATURE(FJCVTZS)
|
||||
ASMJIT_ARM_FEATURE(I8MM)
|
||||
ASMJIT_ARM_FEATURE(RCPC_IMMO)
|
||||
ASMJIT_ARM_FEATURE(RDM)
|
||||
ASMJIT_ARM_FEATURE(PMU)
|
||||
ASMJIT_ARM_FEATURE(PMULL)
|
||||
ASMJIT_ARM_FEATURE(RNG)
|
||||
ASMJIT_ARM_FEATURE(SB)
|
||||
ASMJIT_ARM_FEATURE(SHA1)
|
||||
ASMJIT_ARM_FEATURE(SHA2)
|
||||
ASMJIT_ARM_FEATURE(SHA3)
|
||||
ASMJIT_ARM_FEATURE(SHA512)
|
||||
ASMJIT_ARM_FEATURE(SM3)
|
||||
ASMJIT_ARM_FEATURE(SM4)
|
||||
ASMJIT_ARM_FEATURE(SSBS)
|
||||
ASMJIT_ARM_FEATURE(SVE)
|
||||
ASMJIT_ARM_FEATURE(SVE_BF16)
|
||||
ASMJIT_ARM_FEATURE(SVE_F32MM)
|
||||
ASMJIT_ARM_FEATURE(SVE_F64MM)
|
||||
ASMJIT_ARM_FEATURE(SVE_I8MM)
|
||||
ASMJIT_ARM_FEATURE(SVE_PMULL)
|
||||
ASMJIT_ARM_FEATURE(SVE2)
|
||||
ASMJIT_ARM_FEATURE(SVE2_AES)
|
||||
ASMJIT_ARM_FEATURE(SVE2_BITPERM)
|
||||
ASMJIT_ARM_FEATURE(SVE2_SHA3)
|
||||
ASMJIT_ARM_FEATURE(SVE2_SM4)
|
||||
ASMJIT_ARM_FEATURE(TME)
|
||||
|
||||
#undef ASMJIT_ARM_FEATURE
|
||||
};
|
||||
|
||||
static_assert(uint32_t(X86::kMaxValue) < kMaxFeatures, "The number of X86 CPU features cannot exceed CpuFeatures::kMaxFeatures");
|
||||
static_assert(uint32_t(ARM::kMaxValue) < kMaxFeatures, "The number of ARM CPU features cannot exceed CpuFeatures::kMaxFeatures");
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
Data _data {};
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
inline CpuFeatures() noexcept {}
|
||||
inline CpuFeatures(const CpuFeatures& other) noexcept = default;
|
||||
inline explicit CpuFeatures(Globals::NoInit_) noexcept {}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline CpuFeatures& operator=(const CpuFeatures& other) noexcept = default;
|
||||
|
||||
inline bool operator==(const CpuFeatures& other) noexcept { return eq(other); }
|
||||
inline bool operator!=(const CpuFeatures& other) noexcept { return !eq(other); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns true if there are no features set.
|
||||
inline bool empty() const noexcept { return _data.empty(); }
|
||||
|
||||
//! Casts this base class into a derived type `T`.
|
||||
template<typename T = Data>
|
||||
inline T& data() noexcept { return static_cast<T&>(_data); }
|
||||
|
||||
//! Casts this base class into a derived type `T` (const).
|
||||
template<typename T = Data>
|
||||
inline const T& data() const noexcept { return static_cast<const T&>(_data); }
|
||||
|
||||
//! Returns CpuFeatures::Data as \ref CpuFeatures::X86.
|
||||
inline X86& x86() noexcept { return data<X86>(); }
|
||||
//! Returns CpuFeatures::Data as \ref CpuFeatures::X86 (const).
|
||||
inline const X86& x86() const noexcept { return data<X86>(); }
|
||||
|
||||
//! Returns CpuFeatures::Data as \ref CpuFeatures::ARM.
|
||||
inline ARM& arm() noexcept { return data<ARM>(); }
|
||||
//! Returns CpuFeatures::Data as \ref CpuFeatures::ARM (const).
|
||||
inline const ARM& arm() const noexcept { return data<ARM>(); }
|
||||
|
||||
//! Returns all features as array of bitwords (see \ref Support::BitWord).
|
||||
inline BitWord* bits() noexcept { return _data.bits(); }
|
||||
//! Returns all features as array of bitwords (const).
|
||||
inline const BitWord* bits() const noexcept { return _data.bits(); }
|
||||
//! Returns the number of BitWords returned by \ref bits().
|
||||
inline size_t bitWordCount() const noexcept { return _data.bitWordCount(); }
|
||||
|
||||
//! Returns \ref Support::BitVectorIterator, that can be used to iterate over all features efficiently.
|
||||
inline Iterator iterator() const noexcept { return _data.iterator(); }
|
||||
|
||||
//! Tests whether the feature `featureId` is present.
|
||||
template<typename FeatureId>
|
||||
inline bool has(const FeatureId& featureId) const noexcept { return _data.has(featureId); }
|
||||
|
||||
//! Tests whether all features as defined by `other` are present.
|
||||
inline bool hasAll(const CpuFeatures& other) const noexcept { return _data.hasAll(other._data); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Manipulation
|
||||
//! \{
|
||||
|
||||
inline void reset() noexcept { _data.reset(); }
|
||||
|
||||
//! Adds the given CPU `featureId` to the list of features.
|
||||
template<typename... Args>
|
||||
inline void add(Args&&... args) noexcept { return _data.add(std::forward<Args>(args)...); }
|
||||
|
||||
//! Adds the given CPU `featureId` to the list of features if `condition` is true.
|
||||
template<typename... Args>
|
||||
inline void addIf(bool condition, Args&&... args) noexcept { return _data.addIf(condition, std::forward<Args>(args)...); }
|
||||
|
||||
//! Removes the given CPU `featureId` from the list of features.
|
||||
template<typename... Args>
|
||||
inline void remove(Args&&... args) noexcept { return _data.remove(std::forward<Args>(args)...); }
|
||||
|
||||
//! Tests whether this CPU features matches `other`.
|
||||
inline bool eq(const CpuFeatures& other) const noexcept { return _data.eq(other._data); }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! CPU information.
|
||||
class CpuInfo {
|
||||
public:
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! Architecture.
|
||||
Arch _arch;
|
||||
//! Sub-architecture.
|
||||
SubArch _subArch;
|
||||
//! True if the CPU was detected, false if the detection failed or it's not available.
|
||||
bool _wasDetected;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved;
|
||||
//! CPU family ID.
|
||||
uint32_t _familyId;
|
||||
//! CPU model ID.
|
||||
uint32_t _modelId;
|
||||
//! CPU brand ID.
|
||||
uint32_t _brandId;
|
||||
//! CPU stepping.
|
||||
uint32_t _stepping;
|
||||
//! Processor type.
|
||||
uint32_t _processorType;
|
||||
//! Maximum number of addressable IDs for logical processors.
|
||||
uint32_t _maxLogicalProcessors;
|
||||
//! Cache line size (in bytes).
|
||||
uint32_t _cacheLineSize;
|
||||
//! Number of hardware threads.
|
||||
uint32_t _hwThreadCount;
|
||||
|
||||
//! CPU vendor string.
|
||||
FixedString<16> _vendor;
|
||||
//! CPU brand string.
|
||||
FixedString<64> _brand;
|
||||
//! CPU features.
|
||||
CpuFeatures _features;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
inline CpuInfo() noexcept { reset(); }
|
||||
inline CpuInfo(const CpuInfo& other) noexcept = default;
|
||||
|
||||
inline explicit CpuInfo(Globals::NoInit_) noexcept
|
||||
: _features(Globals::NoInit) {};
|
||||
|
||||
//! Returns the host CPU information.
|
||||
ASMJIT_API static const CpuInfo& host() noexcept;
|
||||
|
||||
//! Initializes CpuInfo architecture and sub-architecture members to `arch` and `subArch`, respectively.
|
||||
inline void initArch(Arch arch, SubArch subArch = SubArch::kUnknown) noexcept {
|
||||
_arch = arch;
|
||||
_subArch = subArch;
|
||||
}
|
||||
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline CpuInfo& operator=(const CpuInfo& other) noexcept = default;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the CPU architecture this information relates to.
|
||||
inline Arch arch() const noexcept { return _arch; }
|
||||
|
||||
//! Returns the CPU sub-architecture this information relates to.
|
||||
inline SubArch subArch() const noexcept { return _subArch; }
|
||||
|
||||
//! Returns whether the CPU was detected successfully.
|
||||
//!
|
||||
//! If the returned value is false it means that AsmJit either failed to detect the CPU or it doesn't have
|
||||
//! implementation targeting the host architecture and operating system.
|
||||
inline bool wasDetected() const noexcept { return _wasDetected; }
|
||||
|
||||
//! Returns the CPU family ID.
|
||||
//!
|
||||
//! Family identifier matches the FamilyId read by using CPUID on X86 architecture.
|
||||
inline uint32_t familyId() const noexcept { return _familyId; }
|
||||
|
||||
//! Returns the CPU model ID.
|
||||
//!
|
||||
//! Family identifier matches the ModelId read by using CPUID on X86 architecture.
|
||||
|
||||
inline uint32_t modelId() const noexcept { return _modelId; }
|
||||
//! Returns the CPU brand id.
|
||||
//!
|
||||
//! Family identifier matches the BrandId read by using CPUID on X86 architecture.
|
||||
inline uint32_t brandId() const noexcept { return _brandId; }
|
||||
|
||||
//! Returns the CPU stepping.
|
||||
//!
|
||||
//! Family identifier matches the Stepping information read by using CPUID on X86 architecture.
|
||||
inline uint32_t stepping() const noexcept { return _stepping; }
|
||||
|
||||
//! Returns the processor type.
|
||||
//!
|
||||
//! Family identifier matches the ProcessorType read by using CPUID on X86 architecture.
|
||||
inline uint32_t processorType() const noexcept { return _processorType; }
|
||||
|
||||
//! Returns the maximum number of logical processors.
|
||||
inline uint32_t maxLogicalProcessors() const noexcept { return _maxLogicalProcessors; }
|
||||
|
||||
//! Returns the size of a cache line flush.
|
||||
inline uint32_t cacheLineSize() const noexcept { return _cacheLineSize; }
|
||||
|
||||
//! Returns number of hardware threads available.
|
||||
inline uint32_t hwThreadCount() const noexcept { return _hwThreadCount; }
|
||||
|
||||
//! Returns a CPU vendor string.
|
||||
inline const char* vendor() const noexcept { return _vendor.str; }
|
||||
//! Tests whether the CPU vendor string is equal to `s`.
|
||||
inline bool isVendor(const char* s) const noexcept { return _vendor.eq(s); }
|
||||
|
||||
//! Returns a CPU brand string.
|
||||
inline const char* brand() const noexcept { return _brand.str; }
|
||||
|
||||
//! Returns CPU features.
|
||||
inline CpuFeatures& features() noexcept { return _features; }
|
||||
//! Returns CPU features (const).
|
||||
inline const CpuFeatures& features() const noexcept { return _features; }
|
||||
|
||||
//! Tests whether the CPU has the given `feature`.
|
||||
template<typename FeatureId>
|
||||
inline bool hasFeature(const FeatureId& featureId) const noexcept { return _features.has(featureId); }
|
||||
|
||||
//! Adds the given CPU `featureId` to the list of features.
|
||||
template<typename... Args>
|
||||
inline void addFeature(Args&&... args) noexcept { return _features.add(std::forward<Args>(args)...); }
|
||||
|
||||
//! Removes the given CPU `featureId` from the list of features.
|
||||
template<typename... Args>
|
||||
inline void removeFeature(Args&&... args) noexcept { return _features.remove(std::forward<Args>(args)...); }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_CPUINFO_H_INCLUDED
|
||||
323
lib/lepton/asmjit/core/emithelper.cpp
Normal file
323
lib/lepton/asmjit/core/emithelper.cpp
Normal file
@ -0,0 +1,323 @@
|
||||
// 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/archtraits.h"
|
||||
#include "../core/emithelper_p.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/funcargscontext_p.h"
|
||||
#include "../core/radefs_p.h"
|
||||
|
||||
// Can be used for debugging...
|
||||
// #define ASMJIT_DUMP_ARGS_ASSIGNMENT
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// BaseEmitHelper - Formatting
|
||||
// ===========================
|
||||
|
||||
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
|
||||
static void dumpFuncValue(String& sb, Arch arch, const FuncValue& value) noexcept {
|
||||
Formatter::formatTypeId(sb, value.typeId());
|
||||
sb.append('@');
|
||||
|
||||
if (value.isIndirect())
|
||||
sb.append('[');
|
||||
|
||||
if (value.isReg())
|
||||
Formatter::formatRegister(sb, 0, nullptr, arch, value.regType(), value.regId());
|
||||
else if (value.isStack())
|
||||
sb.appendFormat("[%d]", value.stackOffset());
|
||||
else
|
||||
sb.append("<none>");
|
||||
|
||||
if (value.isIndirect())
|
||||
sb.append(']');
|
||||
}
|
||||
|
||||
static void dumpAssignment(String& sb, const FuncArgsContext& ctx) noexcept {
|
||||
typedef FuncArgsContext::Var Var;
|
||||
|
||||
Arch arch = ctx.arch();
|
||||
uint32_t varCount = ctx.varCount();
|
||||
|
||||
for (uint32_t i = 0; i < varCount; i++) {
|
||||
const Var& var = ctx.var(i);
|
||||
const FuncValue& dst = var.out;
|
||||
const FuncValue& cur = var.cur;
|
||||
|
||||
sb.appendFormat("Var%u: ", i);
|
||||
dumpFuncValue(sb, arch, dst);
|
||||
sb.append(" <- ");
|
||||
dumpFuncValue(sb, arch, cur);
|
||||
|
||||
if (var.isDone())
|
||||
sb.append(" {Done}");
|
||||
|
||||
sb.append('\n');
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// BaseEmitHelper - EmitArgsAssignment
|
||||
// ===================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error BaseEmitHelper::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
|
||||
typedef FuncArgsContext::Var Var;
|
||||
typedef FuncArgsContext::WorkData WorkData;
|
||||
|
||||
enum WorkFlags : uint32_t {
|
||||
kWorkNone = 0x00,
|
||||
kWorkDidSome = 0x01,
|
||||
kWorkPending = 0x02,
|
||||
kWorkPostponed = 0x04
|
||||
};
|
||||
|
||||
Arch arch = frame.arch();
|
||||
const ArchTraits& archTraits = ArchTraits::byArch(arch);
|
||||
|
||||
RAConstraints constraints;
|
||||
FuncArgsContext ctx;
|
||||
|
||||
ASMJIT_PROPAGATE(constraints.init(arch));
|
||||
ASMJIT_PROPAGATE(ctx.initWorkData(frame, args, &constraints));
|
||||
|
||||
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
|
||||
{
|
||||
String sb;
|
||||
dumpAssignment(sb, ctx);
|
||||
printf("%s\n", sb.data());
|
||||
}
|
||||
#endif
|
||||
|
||||
auto& workData = ctx._workData;
|
||||
uint32_t varCount = ctx._varCount;
|
||||
uint32_t saVarId = ctx._saVarId;
|
||||
|
||||
BaseReg sp = BaseReg(_emitter->_gpSignature, archTraits.spRegId());
|
||||
BaseReg sa = sp;
|
||||
|
||||
if (frame.hasDynamicAlignment()) {
|
||||
if (frame.hasPreservedFP())
|
||||
sa.setId(archTraits.fpRegId());
|
||||
else
|
||||
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
|
||||
}
|
||||
|
||||
// Register to stack and stack to stack moves must be first as now we have
|
||||
// the biggest chance of having as many as possible unassigned registers.
|
||||
|
||||
if (ctx._stackDstMask) {
|
||||
// Base address of all arguments passed by stack.
|
||||
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
|
||||
BaseMem baseStackPtr(sp, 0);
|
||||
|
||||
for (uint32_t varId = 0; varId < varCount; varId++) {
|
||||
Var& var = ctx._vars[varId];
|
||||
|
||||
if (!var.out.isStack())
|
||||
continue;
|
||||
|
||||
FuncValue& cur = var.cur;
|
||||
FuncValue& out = var.out;
|
||||
|
||||
ASMJIT_ASSERT(cur.isReg() || cur.isStack());
|
||||
BaseReg reg;
|
||||
|
||||
BaseMem dstStackPtr = baseStackPtr.cloneAdjusted(out.stackOffset());
|
||||
BaseMem srcStackPtr = baseArgPtr.cloneAdjusted(cur.stackOffset());
|
||||
|
||||
if (cur.isIndirect()) {
|
||||
if (cur.isStack()) {
|
||||
// TODO: Indirect stack.
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
}
|
||||
else {
|
||||
srcStackPtr.setBaseId(cur.regId());
|
||||
}
|
||||
}
|
||||
|
||||
if (cur.isReg() && !cur.isIndirect()) {
|
||||
WorkData& wd = workData[archTraits.regTypeToGroup(cur.regType())];
|
||||
uint32_t regId = cur.regId();
|
||||
|
||||
reg.setSignatureAndId(archTraits.regTypeToSignature(cur.regType()), regId);
|
||||
wd.unassign(varId, regId);
|
||||
}
|
||||
else {
|
||||
// Stack to reg move - tricky since we move stack to stack we can decide which register to use. In general
|
||||
// we follow the rule that IntToInt moves will use GP regs with possibility to signature or zero extend,
|
||||
// and all other moves will either use GP or VEC regs depending on the size of the move.
|
||||
OperandSignature signature = getSuitableRegForMemToMemMove(arch, out.typeId(), cur.typeId());
|
||||
if (ASMJIT_UNLIKELY(!signature.isValid()))
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
WorkData& wd = workData[signature.regGroup()];
|
||||
RegMask availableRegs = wd.availableRegs();
|
||||
if (ASMJIT_UNLIKELY(!availableRegs))
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
uint32_t availableId = Support::ctz(availableRegs);
|
||||
reg.setSignatureAndId(signature, availableId);
|
||||
|
||||
ASMJIT_PROPAGATE(emitArgMove(reg, out.typeId(), srcStackPtr, cur.typeId()));
|
||||
}
|
||||
|
||||
if (cur.isIndirect() && cur.isReg())
|
||||
workData[RegGroup::kGp].unassign(varId, cur.regId());
|
||||
|
||||
// Register to stack move.
|
||||
ASMJIT_PROPAGATE(emitRegMove(dstStackPtr, reg, cur.typeId()));
|
||||
var.markDone();
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle all registers that are currently assigned accordingly to target assignment.
|
||||
|
||||
uint32_t workFlags = kWorkNone;
|
||||
for (;;) {
|
||||
for (uint32_t varId = 0; varId < varCount; varId++) {
|
||||
Var& var = ctx._vars[varId];
|
||||
if (var.isDone() || !var.cur.isReg())
|
||||
continue;
|
||||
|
||||
FuncValue& cur = var.cur;
|
||||
FuncValue& out = var.out;
|
||||
|
||||
RegGroup curGroup = archTraits.regTypeToGroup(cur.regType());
|
||||
RegGroup outGroup = archTraits.regTypeToGroup(out.regType());
|
||||
|
||||
uint32_t curId = cur.regId();
|
||||
uint32_t outId = out.regId();
|
||||
|
||||
if (curGroup != outGroup) {
|
||||
// TODO: Conversion is not supported.
|
||||
return DebugUtils::errored(kErrorInvalidAssignment);
|
||||
}
|
||||
else {
|
||||
WorkData& wd = workData[outGroup];
|
||||
if (!wd.isAssigned(outId)) {
|
||||
EmitMove:
|
||||
ASMJIT_PROPAGATE(
|
||||
emitArgMove(
|
||||
BaseReg(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(),
|
||||
BaseReg(archTraits.regTypeToSignature(cur.regType()), curId), cur.typeId()));
|
||||
|
||||
wd.reassign(varId, outId, curId);
|
||||
cur.initReg(out.regType(), outId, out.typeId());
|
||||
|
||||
if (outId == out.regId())
|
||||
var.markDone();
|
||||
workFlags |= kWorkDidSome | kWorkPending;
|
||||
}
|
||||
else {
|
||||
uint32_t altId = wd._physToVarId[outId];
|
||||
Var& altVar = ctx._vars[altId];
|
||||
|
||||
if (!altVar.out.isInitialized() || (altVar.out.isReg() && altVar.out.regId() == curId)) {
|
||||
// Only few architectures provide swap operations, and only for few register groups.
|
||||
if (archTraits.hasInstRegSwap(curGroup)) {
|
||||
RegType highestType = Support::max(cur.regType(), altVar.cur.regType());
|
||||
if (Support::isBetween(highestType, RegType::kGp8Lo, RegType::kGp16))
|
||||
highestType = RegType::kGp32;
|
||||
|
||||
OperandSignature signature = archTraits.regTypeToSignature(highestType);
|
||||
ASMJIT_PROPAGATE(
|
||||
emitRegSwap(BaseReg(signature, outId), BaseReg(signature, curId)));
|
||||
|
||||
wd.swap(varId, curId, altId, outId);
|
||||
cur.setRegId(outId);
|
||||
var.markDone();
|
||||
altVar.cur.setRegId(curId);
|
||||
|
||||
if (altVar.out.isInitialized())
|
||||
altVar.markDone();
|
||||
workFlags |= kWorkDidSome;
|
||||
}
|
||||
else {
|
||||
// If there is a scratch register it can be used to perform the swap.
|
||||
RegMask availableRegs = wd.availableRegs();
|
||||
if (availableRegs) {
|
||||
RegMask inOutRegs = wd.dstRegs();
|
||||
if (availableRegs & ~inOutRegs)
|
||||
availableRegs &= ~inOutRegs;
|
||||
outId = Support::ctz(availableRegs);
|
||||
goto EmitMove;
|
||||
}
|
||||
else {
|
||||
workFlags |= kWorkPending;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
workFlags |= kWorkPending;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(workFlags & kWorkPending))
|
||||
break;
|
||||
|
||||
// If we did nothing twice it means that something is really broken.
|
||||
if ((workFlags & (kWorkDidSome | kWorkPostponed)) == kWorkPostponed)
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
workFlags = (workFlags & kWorkDidSome) ? kWorkNone : kWorkPostponed;
|
||||
}
|
||||
|
||||
// Load arguments passed by stack into registers. This is pretty simple and
|
||||
// it never requires multiple iterations like the previous phase.
|
||||
|
||||
if (ctx._hasStackSrc) {
|
||||
uint32_t iterCount = 1;
|
||||
if (frame.hasDynamicAlignment() && !frame.hasPreservedFP())
|
||||
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
|
||||
|
||||
// Base address of all arguments passed by stack.
|
||||
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
|
||||
|
||||
for (uint32_t iter = 0; iter < iterCount; iter++) {
|
||||
for (uint32_t varId = 0; varId < varCount; varId++) {
|
||||
Var& var = ctx._vars[varId];
|
||||
if (var.isDone())
|
||||
continue;
|
||||
|
||||
if (var.cur.isStack()) {
|
||||
ASMJIT_ASSERT(var.out.isReg());
|
||||
|
||||
uint32_t outId = var.out.regId();
|
||||
RegType outType = var.out.regType();
|
||||
|
||||
RegGroup group = archTraits.regTypeToGroup(outType);
|
||||
WorkData& wd = workData[group];
|
||||
|
||||
if (outId == sa.id() && group == RegGroup::kGp) {
|
||||
// This register will be processed last as we still need `saRegId`.
|
||||
if (iterCount == 1) {
|
||||
iterCount++;
|
||||
continue;
|
||||
}
|
||||
wd.unassign(wd._physToVarId[outId], outId);
|
||||
}
|
||||
|
||||
BaseReg dstReg = BaseReg(archTraits.regTypeToSignature(outType), outId);
|
||||
BaseMem srcMem = baseArgPtr.cloneAdjusted(var.cur.stackOffset());
|
||||
|
||||
ASMJIT_PROPAGATE(emitArgMove(
|
||||
dstReg, var.out.typeId(),
|
||||
srcMem, var.cur.typeId()));
|
||||
|
||||
wd.assign(varId, outId);
|
||||
var.cur.initReg(outType, outId, var.cur.typeId(), FuncValue::kFlagIsDone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
58
lib/lepton/asmjit/core/emithelper_p.h
Normal file
58
lib/lepton/asmjit/core/emithelper_p.h
Normal file
@ -0,0 +1,58 @@
|
||||
// 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_EMITHELPER_P_H_INCLUDED
|
||||
#define ASMJIT_CORE_EMITHELPER_P_H_INCLUDED
|
||||
|
||||
#include "../core/emitter.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
//! Helper class that provides utilities for each supported architecture.
|
||||
class BaseEmitHelper {
|
||||
public:
|
||||
BaseEmitter* _emitter;
|
||||
|
||||
inline explicit BaseEmitHelper(BaseEmitter* emitter = nullptr) noexcept
|
||||
: _emitter(emitter) {}
|
||||
|
||||
inline BaseEmitter* emitter() const noexcept { return _emitter; }
|
||||
inline void setEmitter(BaseEmitter* emitter) noexcept { _emitter = emitter; }
|
||||
|
||||
//! Emits a pure move operation between two registers or the same type or between a register and its home
|
||||
//! slot. This function does not handle register conversion.
|
||||
virtual Error emitRegMove(
|
||||
const Operand_& dst_,
|
||||
const Operand_& src_, TypeId typeId, const char* comment = nullptr) = 0;
|
||||
|
||||
//! Emits swap between two registers.
|
||||
virtual Error emitRegSwap(
|
||||
const BaseReg& a,
|
||||
const BaseReg& b, const char* comment = nullptr) = 0;
|
||||
|
||||
//! Emits move from a function argument (either register or stack) to a register.
|
||||
//!
|
||||
//! This function can handle the necessary conversion from one argument to another, and from one register type
|
||||
//! to another, if it's possible. Any attempt of conversion that requires third register of a different group
|
||||
//! (for example conversion from K to MMX on X86/X64) will fail.
|
||||
virtual Error emitArgMove(
|
||||
const BaseReg& dst_, TypeId dstTypeId,
|
||||
const Operand_& src_, TypeId srcTypeId, const char* comment = nullptr) = 0;
|
||||
|
||||
Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
|
||||
};
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_EMITHELPER_P_H_INCLUDED
|
||||
333
lib/lepton/asmjit/core/emitter.cpp
Normal file
333
lib/lepton/asmjit/core/emitter.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
// 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/emitterutils_p.h"
|
||||
#include "../core/errorhandler.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// BaseEmitter - Construction & Destruction
|
||||
// ========================================
|
||||
|
||||
BaseEmitter::BaseEmitter(EmitterType emitterType) noexcept
|
||||
: _emitterType(emitterType) {}
|
||||
|
||||
BaseEmitter::~BaseEmitter() noexcept {
|
||||
if (_code) {
|
||||
_addEmitterFlags(EmitterFlags::kDestroyed);
|
||||
_code->detach(this);
|
||||
}
|
||||
}
|
||||
|
||||
// BaseEmitter - Finalize
|
||||
// ======================
|
||||
|
||||
Error BaseEmitter::finalize() {
|
||||
// Does nothing by default, overridden by `BaseBuilder` and `BaseCompiler`.
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// BaseEmitter - Internals
|
||||
// =======================
|
||||
|
||||
static constexpr EmitterFlags kEmitterPreservedFlags = EmitterFlags::kOwnLogger | EmitterFlags::kOwnErrorHandler;
|
||||
|
||||
static ASMJIT_NOINLINE void BaseEmitter_updateForcedOptions(BaseEmitter* self) noexcept {
|
||||
bool emitComments = false;
|
||||
bool hasDiagnosticOptions = false;
|
||||
|
||||
if (self->emitterType() == EmitterType::kAssembler) {
|
||||
// Assembler: Don't emit comments if logger is not attached.
|
||||
emitComments = self->_code != nullptr && self->_logger != nullptr;
|
||||
hasDiagnosticOptions = self->hasDiagnosticOption(DiagnosticOptions::kValidateAssembler);
|
||||
}
|
||||
else {
|
||||
// Builder/Compiler: Always emit comments, we cannot assume they won't be used.
|
||||
emitComments = self->_code != nullptr;
|
||||
hasDiagnosticOptions = self->hasDiagnosticOption(DiagnosticOptions::kValidateIntermediate);
|
||||
}
|
||||
|
||||
if (emitComments)
|
||||
self->_addEmitterFlags(EmitterFlags::kLogComments);
|
||||
else
|
||||
self->_clearEmitterFlags(EmitterFlags::kLogComments);
|
||||
|
||||
// The reserved option tells emitter (Assembler/Builder/Compiler) that there may be either a border
|
||||
// case (CodeHolder not attached, for example) or that logging or validation is required.
|
||||
if (self->_code == nullptr || self->_logger || hasDiagnosticOptions)
|
||||
self->_forcedInstOptions |= InstOptions::kReserved;
|
||||
else
|
||||
self->_forcedInstOptions &= ~InstOptions::kReserved;
|
||||
}
|
||||
|
||||
// BaseEmitter - Diagnostic Options
|
||||
// ================================
|
||||
|
||||
void BaseEmitter::addDiagnosticOptions(DiagnosticOptions options) noexcept {
|
||||
_diagnosticOptions |= options;
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
}
|
||||
|
||||
void BaseEmitter::clearDiagnosticOptions(DiagnosticOptions options) noexcept {
|
||||
_diagnosticOptions &= ~options;
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
}
|
||||
|
||||
// BaseEmitter - Logging
|
||||
// =====================
|
||||
|
||||
void BaseEmitter::setLogger(Logger* logger) noexcept {
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (logger) {
|
||||
_logger = logger;
|
||||
_addEmitterFlags(EmitterFlags::kOwnLogger);
|
||||
}
|
||||
else {
|
||||
_logger = nullptr;
|
||||
_clearEmitterFlags(EmitterFlags::kOwnLogger);
|
||||
if (_code)
|
||||
_logger = _code->logger();
|
||||
}
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
#else
|
||||
DebugUtils::unused(logger);
|
||||
#endif
|
||||
}
|
||||
|
||||
// BaseEmitter - Error Handling
|
||||
// ============================
|
||||
|
||||
void BaseEmitter::setErrorHandler(ErrorHandler* errorHandler) noexcept {
|
||||
if (errorHandler) {
|
||||
_errorHandler = errorHandler;
|
||||
_addEmitterFlags(EmitterFlags::kOwnErrorHandler);
|
||||
}
|
||||
else {
|
||||
_errorHandler = nullptr;
|
||||
_clearEmitterFlags(EmitterFlags::kOwnErrorHandler);
|
||||
if (_code)
|
||||
_errorHandler = _code->errorHandler();
|
||||
}
|
||||
}
|
||||
|
||||
Error BaseEmitter::reportError(Error err, const char* message) {
|
||||
ErrorHandler* eh = _errorHandler;
|
||||
if (eh) {
|
||||
if (!message)
|
||||
message = DebugUtils::errorAsString(err);
|
||||
eh->handleError(err, message, this);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// BaseEmitter - Labels
|
||||
// ====================
|
||||
|
||||
Label BaseEmitter::labelByName(const char* name, size_t nameSize, uint32_t parentId) noexcept {
|
||||
return Label(_code ? _code->labelIdByName(name, nameSize, parentId) : Globals::kInvalidId);
|
||||
}
|
||||
|
||||
bool BaseEmitter::isLabelValid(uint32_t labelId) const noexcept {
|
||||
return _code && labelId < _code->labelCount();
|
||||
}
|
||||
|
||||
// BaseEmitter - Emit (Low-Level)
|
||||
// ==============================
|
||||
|
||||
using EmitterUtils::noExt;
|
||||
|
||||
Error BaseEmitter::_emitI(InstId instId) {
|
||||
return _emit(instId, noExt[0], noExt[1], noExt[2], noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0) {
|
||||
return _emit(instId, o0, noExt[1], noExt[2], noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1) {
|
||||
return _emit(instId, o0, o1, noExt[2], noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2) {
|
||||
return _emit(instId, o0, o1, o2, noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
||||
Operand_ opExt[3] = { o3 };
|
||||
return _emit(instId, o0, o1, o2, opExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4) {
|
||||
Operand_ opExt[3] = { o3, o4 };
|
||||
return _emit(instId, o0, o1, o2, opExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) {
|
||||
Operand_ opExt[3] = { o3, o4, o5 };
|
||||
return _emit(instId, o0, o1, o2, opExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitOpArray(InstId instId, const Operand_* operands, size_t opCount) {
|
||||
const Operand_* op = operands;
|
||||
Operand_ opExt[3];
|
||||
|
||||
switch (opCount) {
|
||||
case 0:
|
||||
return _emit(instId, noExt[0], noExt[1], noExt[2], noExt);
|
||||
|
||||
case 1:
|
||||
return _emit(instId, op[0], noExt[1], noExt[2], noExt);
|
||||
|
||||
case 2:
|
||||
return _emit(instId, op[0], op[1], noExt[2], noExt);
|
||||
|
||||
case 3:
|
||||
return _emit(instId, op[0], op[1], op[2], noExt);
|
||||
|
||||
case 4:
|
||||
opExt[0] = op[3];
|
||||
opExt[1].reset();
|
||||
opExt[2].reset();
|
||||
return _emit(instId, op[0], op[1], op[2], opExt);
|
||||
|
||||
case 5:
|
||||
opExt[0] = op[3];
|
||||
opExt[1] = op[4];
|
||||
opExt[2].reset();
|
||||
return _emit(instId, op[0], op[1], op[2], opExt);
|
||||
|
||||
case 6:
|
||||
return _emit(instId, op[0], op[1], op[2], op + 3);
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
// BaseEmitter - Emit Utilities
|
||||
// ============================
|
||||
|
||||
Error BaseEmitter::emitProlog(const FuncFrame& frame) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
return _funcs.emitProlog(this, frame);
|
||||
}
|
||||
|
||||
Error BaseEmitter::emitEpilog(const FuncFrame& frame) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
return _funcs.emitEpilog(this, frame);
|
||||
}
|
||||
|
||||
Error BaseEmitter::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
return _funcs.emitArgsAssignment(this, frame, args);
|
||||
}
|
||||
|
||||
// BaseEmitter - Comment
|
||||
// =====================
|
||||
|
||||
Error BaseEmitter::commentf(const char* fmt, ...) {
|
||||
if (!hasEmitterFlag(EmitterFlags::kLogComments)) {
|
||||
if (!hasEmitterFlag(EmitterFlags::kAttached))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
StringTmp<1024> sb;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
Error err = sb.appendVFormat(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
ASMJIT_PROPAGATE(err);
|
||||
return comment(sb.data(), sb.size());
|
||||
#else
|
||||
DebugUtils::unused(fmt);
|
||||
return kErrorOk;
|
||||
#endif
|
||||
}
|
||||
|
||||
Error BaseEmitter::commentv(const char* fmt, va_list ap) {
|
||||
if (!hasEmitterFlag(EmitterFlags::kLogComments)) {
|
||||
if (!hasEmitterFlag(EmitterFlags::kAttached))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
StringTmp<1024> sb;
|
||||
Error err = sb.appendVFormat(fmt, ap);
|
||||
|
||||
ASMJIT_PROPAGATE(err);
|
||||
return comment(sb.data(), sb.size());
|
||||
#else
|
||||
DebugUtils::unused(fmt, ap);
|
||||
return kErrorOk;
|
||||
#endif
|
||||
}
|
||||
|
||||
// BaseEmitter - Events
|
||||
// ====================
|
||||
|
||||
Error BaseEmitter::onAttach(CodeHolder* code) noexcept {
|
||||
_code = code;
|
||||
_environment = code->environment();
|
||||
_addEmitterFlags(EmitterFlags::kAttached);
|
||||
|
||||
const ArchTraits& archTraits = ArchTraits::byArch(code->arch());
|
||||
RegType nativeRegType = Environment::is32Bit(code->arch()) ? RegType::kGp32 : RegType::kGp64;
|
||||
_gpSignature = archTraits.regTypeToSignature(nativeRegType);
|
||||
|
||||
onSettingsUpdated();
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseEmitter::onDetach(CodeHolder* code) noexcept {
|
||||
DebugUtils::unused(code);
|
||||
|
||||
if (!hasOwnLogger())
|
||||
_logger = nullptr;
|
||||
|
||||
if (!hasOwnErrorHandler())
|
||||
_errorHandler = nullptr;
|
||||
|
||||
_clearEmitterFlags(~kEmitterPreservedFlags);
|
||||
_forcedInstOptions = InstOptions::kReserved;
|
||||
_privateData = 0;
|
||||
|
||||
_environment.reset();
|
||||
_gpSignature.reset();
|
||||
|
||||
_instOptions = InstOptions::kNone;
|
||||
_extraReg.reset();
|
||||
_inlineComment = nullptr;
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
void BaseEmitter::onSettingsUpdated() noexcept {
|
||||
// Only called when attached to CodeHolder by CodeHolder.
|
||||
ASMJIT_ASSERT(_code != nullptr);
|
||||
|
||||
if (!hasOwnLogger())
|
||||
_logger = _code->logger();
|
||||
|
||||
if (!hasOwnErrorHandler())
|
||||
_errorHandler = _code->errorHandler();
|
||||
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
741
lib/lepton/asmjit/core/emitter.h
Normal file
741
lib/lepton/asmjit/core/emitter.h
Normal file
@ -0,0 +1,741 @@
|
||||
// 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_EMITTER_H_INCLUDED
|
||||
#define ASMJIT_CORE_EMITTER_H_INCLUDED
|
||||
|
||||
#include "../core/archtraits.h"
|
||||
#include "../core/codeholder.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/inst.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
class ConstPool;
|
||||
class FuncFrame;
|
||||
class FuncArgsAssignment;
|
||||
|
||||
//! Align mode, used by \ref BaseEmitter::align().
|
||||
enum class AlignMode : uint8_t {
|
||||
//! Align executable code.
|
||||
kCode = 0,
|
||||
//! Align non-executable code.
|
||||
kData = 1,
|
||||
//! Align by a sequence of zeros.
|
||||
kZero = 2,
|
||||
|
||||
//! Maximum value of `AlignMode`.
|
||||
kMaxValue = kZero
|
||||
};
|
||||
|
||||
//! Emitter type used by \ref BaseEmitter.
|
||||
enum class EmitterType : uint8_t {
|
||||
//! Unknown or uninitialized.
|
||||
kNone = 0,
|
||||
//! Emitter inherits from \ref BaseAssembler.
|
||||
kAssembler = 1,
|
||||
//! Emitter inherits from \ref BaseBuilder.
|
||||
kBuilder = 2,
|
||||
//! Emitter inherits from \ref BaseCompiler.
|
||||
kCompiler = 3,
|
||||
|
||||
//! Maximum value of `EmitterType`.
|
||||
kMaxValue = kCompiler
|
||||
};
|
||||
|
||||
//! Emitter flags, used by \ref BaseEmitter.
|
||||
enum class EmitterFlags : uint8_t {
|
||||
//! No flags.
|
||||
kNone = 0u,
|
||||
//! Emitter is attached to CodeHolder.
|
||||
kAttached = 0x01u,
|
||||
//! The emitter must emit comments.
|
||||
kLogComments = 0x08u,
|
||||
//! The emitter has its own \ref Logger (not propagated from \ref CodeHolder).
|
||||
kOwnLogger = 0x10u,
|
||||
//! The emitter has its own \ref ErrorHandler (not propagated from \ref CodeHolder).
|
||||
kOwnErrorHandler = 0x20u,
|
||||
//! The emitter was finalized.
|
||||
kFinalized = 0x40u,
|
||||
//! The emitter was destroyed.
|
||||
//!
|
||||
//! This flag is used for a very short time when an emitter is being destroyed by
|
||||
//! CodeHolder.
|
||||
kDestroyed = 0x80u
|
||||
};
|
||||
ASMJIT_DEFINE_ENUM_FLAGS(EmitterFlags)
|
||||
|
||||
//! Encoding options.
|
||||
enum class EncodingOptions : uint32_t {
|
||||
//! No encoding options.
|
||||
kNone = 0,
|
||||
|
||||
//! Emit instructions that are optimized for size, if possible.
|
||||
//!
|
||||
//! Default: false.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
//!
|
||||
//! When this option is set it the assembler will try to fix instructions if possible into operation equivalent
|
||||
//! instructions that take less bytes by taking advantage of implicit zero extension. For example instruction
|
||||
//! like `mov r64, imm` and `and r64, imm` can be translated to `mov r32, imm` and `and r32, imm` when the
|
||||
//! immediate constant is lesser than `2^31`.
|
||||
kOptimizeForSize = 0x00000001u,
|
||||
|
||||
//! Emit optimized code-alignment sequences.
|
||||
//!
|
||||
//! Default: false.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
//!
|
||||
//! Default align sequence used by X86 architecture is one-byte (0x90) opcode that is often shown by disassemblers
|
||||
//! as NOP. However there are more optimized align sequences for 2-11 bytes that may execute faster on certain CPUs.
|
||||
//! If this feature is enabled AsmJit will generate specialized sequences for alignment between 2 to 11 bytes.
|
||||
kOptimizedAlign = 0x00000002u,
|
||||
|
||||
//! Emit jump-prediction hints.
|
||||
//!
|
||||
//! Default: false.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
//!
|
||||
//! Jump prediction is usually based on the direction of the jump. If the jump is backward it is usually predicted as
|
||||
//! taken; and if the jump is forward it is usually predicted as not-taken. The reason is that loops generally use
|
||||
//! backward jumps and conditions usually use forward jumps. However this behavior can be overridden by using
|
||||
//! instruction prefixes. If this option is enabled these hints will be emitted.
|
||||
//!
|
||||
//! This feature is disabled by default, because the only processor that used to take into consideration prediction
|
||||
//! hints was P4. Newer processors implement heuristics for branch prediction and ignore static hints. This means
|
||||
//! that this feature can be only used for annotation purposes.
|
||||
kPredictedJumps = 0x00000010u
|
||||
};
|
||||
ASMJIT_DEFINE_ENUM_FLAGS(EncodingOptions)
|
||||
|
||||
//! Diagnostic options are used to tell emitters and their passes to perform diagnostics when emitting or processing
|
||||
//! user code. These options control validation and extra diagnostics that can be performed by higher level emitters.
|
||||
//!
|
||||
//! Instruction Validation
|
||||
//! ----------------------
|
||||
//!
|
||||
//! \ref BaseAssembler implementation perform by default only basic checks that are necessary to identify all
|
||||
//! variations of an instruction so the correct encoding can be selected. This is fine for production-ready code
|
||||
//! as the assembler doesn't have to perform checks that would slow it down. However, sometimes these checks are
|
||||
//! beneficial especially when the project that uses AsmJit is in a development phase, in which mistakes happen
|
||||
//! often. To make the experience of using AsmJit seamless it offers validation features that can be controlled
|
||||
//! by \ref DiagnosticOptions.
|
||||
//!
|
||||
//! Compiler Diagnostics
|
||||
//! --------------------
|
||||
//!
|
||||
//! Diagnostic options work with \ref BaseCompiler passes (precisely with its register allocation pass). These options
|
||||
//! can be used to enable logging of all operations that the Compiler does.
|
||||
enum class DiagnosticOptions : uint32_t {
|
||||
//! No validation options.
|
||||
kNone = 0,
|
||||
|
||||
//! Perform strict validation in \ref BaseAssembler::emit() implementations.
|
||||
//!
|
||||
//! This flag ensures that each instruction is checked before it's encoded into a binary representation. This flag
|
||||
//! is only relevant for \ref BaseAssembler implementations, but can be set in any other emitter type, in that case
|
||||
//! if that emitter needs to create an assembler on its own, for the purpose of \ref BaseEmitter::finalize() it
|
||||
//! would propagate this flag to such assembler so all instructions passed to it are explicitly validated.
|
||||
//!
|
||||
//! Default: false.
|
||||
kValidateAssembler = 0x00000001u,
|
||||
|
||||
//! Perform strict validation in \ref BaseBuilder::emit() and \ref BaseCompiler::emit() implementations.
|
||||
//!
|
||||
//! This flag ensures that each instruction is checked before an \ref InstNode representing the instruction is
|
||||
//! created by \ref BaseBuilder or \ref BaseCompiler. This option could be more useful than \ref kValidateAssembler
|
||||
//! in cases in which there is an invalid instruction passed to an assembler, which was invalid much earlier, most
|
||||
//! likely when such instruction was passed to Builder/Compiler.
|
||||
//!
|
||||
//! This is a separate option that was introduced, because it's possible to manipulate the instruction stream
|
||||
//! emitted by \ref BaseBuilder and \ref BaseCompiler - this means that it's allowed to emit invalid instructions
|
||||
//! (for example with missing operands) that will be fixed later before finalizing it.
|
||||
//!
|
||||
//! Default: false.
|
||||
kValidateIntermediate = 0x00000002u,
|
||||
|
||||
//! Annotate all nodes processed by register allocator (Compiler/RA).
|
||||
//!
|
||||
//! \note Annotations don't need debug options, however, some debug options like `kRADebugLiveness` may influence
|
||||
//! their output (for example the mentioned option would add liveness information to per-instruction annotation).
|
||||
kRAAnnotate = 0x00000080u,
|
||||
|
||||
//! Debug CFG generation and other related algorithms / operations (Compiler/RA).
|
||||
kRADebugCFG = 0x00000100u,
|
||||
|
||||
//! Debug liveness analysis (Compiler/RA).
|
||||
kRADebugLiveness = 0x00000200u,
|
||||
|
||||
//! Debug register allocation assignment (Compiler/RA).
|
||||
kRADebugAssignment = 0x00000400u,
|
||||
|
||||
//! Debug the removal of code part of unreachable blocks.
|
||||
kRADebugUnreachable = 0x00000800u,
|
||||
|
||||
//! Enable all debug options (Compiler/RA).
|
||||
kRADebugAll = 0x0000FF00u,
|
||||
};
|
||||
ASMJIT_DEFINE_ENUM_FLAGS(DiagnosticOptions)
|
||||
|
||||
//! Provides a base foundation to emitting code - specialized by \ref BaseAssembler and \ref BaseBuilder.
|
||||
class ASMJIT_VIRTAPI BaseEmitter {
|
||||
public:
|
||||
ASMJIT_BASE_CLASS(BaseEmitter)
|
||||
|
||||
//! \name Members
|
||||
//! \{
|
||||
|
||||
//! See \ref EmitterType.
|
||||
EmitterType _emitterType = EmitterType::kNone;
|
||||
//! See \ref EmitterFlags.
|
||||
EmitterFlags _emitterFlags = EmitterFlags::kNone;
|
||||
//! 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
|
||||
//! changed.
|
||||
ValidationFlags _validationFlags = ValidationFlags::kNone;
|
||||
//! Validation options.
|
||||
DiagnosticOptions _diagnosticOptions = DiagnosticOptions::kNone;
|
||||
|
||||
//! All supported architectures in a bit-mask, where LSB is the bit with a zero index.
|
||||
uint64_t _archMask = 0;
|
||||
|
||||
//! Encoding options.
|
||||
EncodingOptions _encodingOptions = EncodingOptions::kNone;
|
||||
|
||||
//! Forced instruction options, combined with \ref _instOptions by \ref emit().
|
||||
InstOptions _forcedInstOptions = InstOptions::kReserved;
|
||||
//! Internal private data used freely by any emitter.
|
||||
uint32_t _privateData = 0;
|
||||
|
||||
//! CodeHolder the emitter is attached to.
|
||||
CodeHolder* _code = nullptr;
|
||||
//! Attached \ref Logger.
|
||||
Logger* _logger = nullptr;
|
||||
//! Attached \ref ErrorHandler.
|
||||
ErrorHandler* _errorHandler = nullptr;
|
||||
|
||||
//! Describes the target environment, matches \ref CodeHolder::environment().
|
||||
Environment _environment {};
|
||||
//! Native GP register signature and signature related information.
|
||||
OperandSignature _gpSignature {};
|
||||
|
||||
//! Next instruction options (affects the next instruction).
|
||||
InstOptions _instOptions = InstOptions::kNone;
|
||||
//! Extra register (op-mask {k} on AVX-512) (affects the next instruction).
|
||||
RegOnly _extraReg {};
|
||||
//! Inline comment of the next instruction (affects the next instruction).
|
||||
const char* _inlineComment = nullptr;
|
||||
|
||||
//! Function callbacks used by emitter implementation.
|
||||
//!
|
||||
//! These are typically shared between Assembler/Builder/Compiler of a single backend.
|
||||
struct Funcs {
|
||||
typedef Error (ASMJIT_CDECL* EmitProlog)(BaseEmitter* emitter, const FuncFrame& frame);
|
||||
typedef Error (ASMJIT_CDECL* EmitEpilog)(BaseEmitter* emitter, const FuncFrame& frame);
|
||||
typedef Error (ASMJIT_CDECL* EmitArgsAssignment)(BaseEmitter* emitter, const FuncFrame& frame, const FuncArgsAssignment& args);
|
||||
|
||||
typedef Error (ASMJIT_CDECL* FormatInstruction)(
|
||||
String& sb,
|
||||
FormatFlags formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
Arch arch,
|
||||
const BaseInst& inst, const Operand_* operands, size_t opCount) ASMJIT_NOEXCEPT_TYPE;
|
||||
|
||||
typedef Error (ASMJIT_CDECL* ValidateFunc)(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) ASMJIT_NOEXCEPT_TYPE;
|
||||
|
||||
//! Emit prolog implementation.
|
||||
EmitProlog emitProlog;
|
||||
//! Emit epilog implementation.
|
||||
EmitEpilog emitEpilog;
|
||||
//! Emit arguments assignment implementation.
|
||||
EmitArgsAssignment emitArgsAssignment;
|
||||
//! Instruction formatter implementation.
|
||||
FormatInstruction formatInstruction;
|
||||
//! Instruction validation implementation.
|
||||
ValidateFunc validate;
|
||||
|
||||
//! Resets all functions to nullptr.
|
||||
inline void reset() noexcept {
|
||||
emitProlog = nullptr;
|
||||
emitEpilog = nullptr;
|
||||
emitArgsAssignment = nullptr;
|
||||
validate = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Funcs _funcs {};
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API explicit BaseEmitter(EmitterType emitterType) noexcept;
|
||||
ASMJIT_API virtual ~BaseEmitter() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Cast
|
||||
//! \{
|
||||
|
||||
template<typename T>
|
||||
inline T* as() noexcept { return reinterpret_cast<T*>(this); }
|
||||
|
||||
template<typename T>
|
||||
inline const T* as() const noexcept { return reinterpret_cast<const T*>(this); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Emitter Type & Flags
|
||||
//! \{
|
||||
|
||||
//! Returns the type of this emitter, see `EmitterType`.
|
||||
inline EmitterType emitterType() const noexcept { return _emitterType; }
|
||||
//! Returns emitter flags , see `Flags`.
|
||||
inline EmitterFlags emitterFlags() const noexcept { return _emitterFlags; }
|
||||
|
||||
//! Tests whether the emitter inherits from `BaseAssembler`.
|
||||
inline bool isAssembler() const noexcept { return _emitterType == EmitterType::kAssembler; }
|
||||
//! Tests whether the emitter inherits from `BaseBuilder`.
|
||||
//!
|
||||
//! \note Both Builder and Compiler emitters would return `true`.
|
||||
inline bool isBuilder() const noexcept { return uint32_t(_emitterType) >= uint32_t(EmitterType::kBuilder); }
|
||||
//! Tests whether the emitter inherits from `BaseCompiler`.
|
||||
inline bool isCompiler() const noexcept { return _emitterType == EmitterType::kCompiler; }
|
||||
|
||||
//! Tests whether the emitter has the given `flag` enabled.
|
||||
inline bool hasEmitterFlag(EmitterFlags flag) const noexcept { return Support::test(_emitterFlags, flag); }
|
||||
//! Tests whether the emitter is finalized.
|
||||
inline bool isFinalized() const noexcept { return hasEmitterFlag(EmitterFlags::kFinalized); }
|
||||
//! Tests whether the emitter is destroyed (only used during destruction).
|
||||
inline bool isDestroyed() const noexcept { return hasEmitterFlag(EmitterFlags::kDestroyed); }
|
||||
|
||||
inline void _addEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags |= flags; }
|
||||
inline void _clearEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags &= _emitterFlags & ~flags; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Target Information
|
||||
//! \{
|
||||
|
||||
//! Returns the CodeHolder this emitter is attached to.
|
||||
inline CodeHolder* code() const noexcept { return _code; }
|
||||
|
||||
//! Returns the target environment.
|
||||
//!
|
||||
//! The returned \ref Environment reference matches \ref CodeHolder::environment().
|
||||
inline const Environment& environment() const noexcept { return _environment; }
|
||||
|
||||
//! Tests whether the target architecture is 32-bit.
|
||||
inline bool is32Bit() const noexcept { return environment().is32Bit(); }
|
||||
//! Tests whether the target architecture is 64-bit.
|
||||
inline bool is64Bit() const noexcept { return environment().is64Bit(); }
|
||||
|
||||
//! Returns the target architecture type.
|
||||
inline Arch arch() const noexcept { return environment().arch(); }
|
||||
//! Returns the target architecture sub-type.
|
||||
inline SubArch subArch() const noexcept { return environment().subArch(); }
|
||||
|
||||
//! Returns the target architecture's GP register size (4 or 8 bytes).
|
||||
inline uint32_t registerSize() const noexcept { return environment().registerSize(); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Initialization & Finalization
|
||||
//! \{
|
||||
|
||||
//! Tests whether the emitter is initialized (i.e. attached to \ref CodeHolder).
|
||||
inline bool isInitialized() const noexcept { return _code != nullptr; }
|
||||
|
||||
//! Finalizes this emitter.
|
||||
//!
|
||||
//! Materializes the content of the emitter by serializing it to the attached \ref CodeHolder through an architecture
|
||||
//! specific \ref BaseAssembler. This function won't do anything if the emitter inherits from \ref BaseAssembler as
|
||||
//! assemblers emit directly to a \ref CodeBuffer held by \ref CodeHolder. However, if this is an emitter that
|
||||
//! inherits from \ref BaseBuilder or \ref BaseCompiler then these emitters need the materialization phase as they
|
||||
//! store their content in a representation not visible to \ref CodeHolder.
|
||||
ASMJIT_API virtual Error finalize();
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Logging
|
||||
//! \{
|
||||
|
||||
//! Tests whether the emitter has a logger.
|
||||
inline bool hasLogger() const noexcept { return _logger != nullptr; }
|
||||
|
||||
//! Tests whether the emitter has its own logger.
|
||||
//!
|
||||
//! Own logger means that it overrides the possible logger that may be used by \ref CodeHolder this emitter is
|
||||
//! attached to.
|
||||
inline bool hasOwnLogger() const noexcept { return hasEmitterFlag(EmitterFlags::kOwnLogger); }
|
||||
|
||||
//! Returns the logger this emitter uses.
|
||||
//!
|
||||
//! The returned logger is either the emitter's own logger or it's logger used by \ref CodeHolder this emitter
|
||||
//! is attached to.
|
||||
inline Logger* logger() const noexcept { return _logger; }
|
||||
|
||||
//! Sets or resets the logger of the emitter.
|
||||
//!
|
||||
//! If the `logger` argument is non-null then the logger will be considered emitter's own logger, see \ref
|
||||
//! hasOwnLogger() for more details. If the given `logger` is null then the emitter will automatically use logger
|
||||
//! that is attached to the \ref CodeHolder this emitter is attached to.
|
||||
ASMJIT_API void setLogger(Logger* logger) noexcept;
|
||||
|
||||
//! Resets the logger of this emitter.
|
||||
//!
|
||||
//! The emitter will bail to using a logger attached to \ref CodeHolder this emitter is attached to, or no logger
|
||||
//! at all if \ref CodeHolder doesn't have one.
|
||||
inline void resetLogger() noexcept { return setLogger(nullptr); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Error Handling
|
||||
//! \{
|
||||
|
||||
//! Tests whether the emitter has an error handler attached.
|
||||
inline bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; }
|
||||
|
||||
//! Tests whether the emitter has its own error handler.
|
||||
//!
|
||||
//! Own error handler means that it overrides the possible error handler that may be used by \ref CodeHolder this
|
||||
//! emitter is attached to.
|
||||
inline bool hasOwnErrorHandler() const noexcept { return hasEmitterFlag(EmitterFlags::kOwnErrorHandler); }
|
||||
|
||||
//! Returns the error handler this emitter uses.
|
||||
//!
|
||||
//! The returned error handler is either the emitter's own error handler or it's error handler used by
|
||||
//! \ref CodeHolder this emitter is attached to.
|
||||
inline ErrorHandler* errorHandler() const noexcept { return _errorHandler; }
|
||||
|
||||
//! Sets or resets the error handler of the emitter.
|
||||
ASMJIT_API void setErrorHandler(ErrorHandler* errorHandler) noexcept;
|
||||
|
||||
//! Resets the error handler.
|
||||
inline void resetErrorHandler() noexcept { setErrorHandler(nullptr); }
|
||||
|
||||
//! Handles the given error in the following way:
|
||||
//! 1. If the emitter has \ref ErrorHandler attached, it calls its \ref ErrorHandler::handleError() member function
|
||||
//! first, and then returns the error. The `handleError()` function may throw.
|
||||
//! 2. if the emitter doesn't have \ref ErrorHandler, the error is simply returned.
|
||||
ASMJIT_API Error reportError(Error err, const char* message = nullptr);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Encoding Options
|
||||
//! \{
|
||||
|
||||
//! Returns encoding options.
|
||||
inline EncodingOptions encodingOptions() const noexcept { return _encodingOptions; }
|
||||
//! Tests whether the encoding `option` is set.
|
||||
inline bool hasEncodingOption(EncodingOptions option) const noexcept { return Support::test(_encodingOptions, option); }
|
||||
|
||||
//! Enables the given encoding `options`.
|
||||
inline void addEncodingOptions(EncodingOptions options) noexcept { _encodingOptions |= options; }
|
||||
//! Disables the given encoding `options`.
|
||||
inline void clearEncodingOptions(EncodingOptions options) noexcept { _encodingOptions &= ~options; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Diagnostic Options
|
||||
//! \{
|
||||
|
||||
//! Returns the emitter's diagnostic options.
|
||||
inline DiagnosticOptions diagnosticOptions() const noexcept { return _diagnosticOptions; }
|
||||
|
||||
//! Tests whether the given `option` is present in the emitter's diagnostic options.
|
||||
inline bool hasDiagnosticOption(DiagnosticOptions option) const noexcept { return Support::test(_diagnosticOptions, option); }
|
||||
|
||||
//! Activates the given diagnostic `options`.
|
||||
//!
|
||||
//! This function is used to activate explicit validation options that will be then used by all emitter
|
||||
//! implementations. There are in general two possibilities:
|
||||
//!
|
||||
//! - Architecture specific assembler is used. In this case a \ref DiagnosticOptions::kValidateAssembler can be
|
||||
//! used to turn on explicit validation that will be used before an instruction is emitted. This means that
|
||||
//! internally an extra step will be performed to make sure that the instruction is correct. This is needed,
|
||||
//! because by default assemblers prefer speed over strictness.
|
||||
//!
|
||||
//! This option should be used in debug builds as it's pretty expensive.
|
||||
//!
|
||||
//! - Architecture specific builder or compiler is used. In this case the user can turn on
|
||||
//! \ref DiagnosticOptions::kValidateIntermediate option that adds explicit validation step before the Builder
|
||||
//! or Compiler creates an \ref InstNode to represent an emitted instruction. Error will be returned if the
|
||||
//! instruction is ill-formed. In addition, also \ref DiagnosticOptions::kValidateAssembler can be used, which
|
||||
//! would not be consumed by Builder / Compiler directly, but it would be propagated to an architecture specific
|
||||
//! \ref BaseAssembler implementation it creates during \ref BaseEmitter::finalize().
|
||||
ASMJIT_API void addDiagnosticOptions(DiagnosticOptions options) noexcept;
|
||||
|
||||
//! Deactivates the given validation `options`.
|
||||
//!
|
||||
//! See \ref addDiagnosticOptions() and \ref DiagnosticOptions for more details.
|
||||
ASMJIT_API void clearDiagnosticOptions(DiagnosticOptions options) noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Instruction Options
|
||||
//! \{
|
||||
|
||||
//! Returns forced instruction options.
|
||||
//!
|
||||
//! Forced instruction options are merged with next instruction options before the instruction is encoded. These
|
||||
//! options have some bits reserved that are used by error handling, logging, and instruction validation purposes.
|
||||
//! Other options are globals that affect each instruction.
|
||||
inline InstOptions forcedInstOptions() const noexcept { return _forcedInstOptions; }
|
||||
|
||||
//! Returns options of the next instruction.
|
||||
inline InstOptions instOptions() const noexcept { return _instOptions; }
|
||||
//! Returns options of the next instruction.
|
||||
inline void setInstOptions(InstOptions options) noexcept { _instOptions = options; }
|
||||
//! Adds options of the next instruction.
|
||||
inline void addInstOptions(InstOptions options) noexcept { _instOptions |= options; }
|
||||
//! Resets options of the next instruction.
|
||||
inline void resetInstOptions() noexcept { _instOptions = InstOptions::kNone; }
|
||||
|
||||
//! Tests whether the extra register operand is valid.
|
||||
inline bool hasExtraReg() const noexcept { return _extraReg.isReg(); }
|
||||
//! Returns an extra operand that will be used by the next instruction (architecture specific).
|
||||
inline const RegOnly& extraReg() const noexcept { return _extraReg; }
|
||||
//! Sets an extra operand that will be used by the next instruction (architecture specific).
|
||||
inline void setExtraReg(const BaseReg& reg) noexcept { _extraReg.init(reg); }
|
||||
//! Sets an extra operand that will be used by the next instruction (architecture specific).
|
||||
inline void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); }
|
||||
//! Resets an extra operand that will be used by the next instruction (architecture specific).
|
||||
inline void resetExtraReg() noexcept { _extraReg.reset(); }
|
||||
|
||||
//! Returns comment/annotation of the next instruction.
|
||||
inline const char* inlineComment() const noexcept { return _inlineComment; }
|
||||
//! Sets comment/annotation of the next instruction.
|
||||
//!
|
||||
//! \note This string is set back to null by `_emit()`, but until that it has to remain valid as the Emitter is not
|
||||
//! required to make a copy of it (and it would be slow to do that for each instruction).
|
||||
inline void setInlineComment(const char* s) noexcept { _inlineComment = s; }
|
||||
//! Resets the comment/annotation to nullptr.
|
||||
inline void resetInlineComment() noexcept { _inlineComment = nullptr; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Sections
|
||||
//! \{
|
||||
|
||||
virtual Error section(Section* section) = 0;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Labels
|
||||
//! \{
|
||||
|
||||
//! Creates a new label.
|
||||
virtual Label newLabel() = 0;
|
||||
//! Creates a new named label.
|
||||
virtual Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, LabelType type = LabelType::kGlobal, uint32_t parentId = Globals::kInvalidId) = 0;
|
||||
|
||||
//! Creates a new anonymous label with a name, which can only be used for debugging purposes.
|
||||
inline Label newAnonymousLabel(const char* name, size_t nameSize = SIZE_MAX) { return newNamedLabel(name, nameSize, LabelType::kAnonymous); }
|
||||
//! Creates a new external label.
|
||||
inline Label newExternalLabel(const char* name, size_t nameSize = SIZE_MAX) { return newNamedLabel(name, nameSize, LabelType::kExternal); }
|
||||
|
||||
//! Returns `Label` by `name`.
|
||||
//!
|
||||
//! Returns invalid Label in case that the name is invalid or label was not found.
|
||||
//!
|
||||
//! \note This function doesn't trigger ErrorHandler in case the name is invalid or no such label exist. You must
|
||||
//! always check the validity of the `Label` returned.
|
||||
ASMJIT_API Label labelByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept;
|
||||
|
||||
//! Binds the `label` to the current position of the current section.
|
||||
//!
|
||||
//! \note Attempt to bind the same label multiple times will return an error.
|
||||
virtual Error bind(const Label& label) = 0;
|
||||
|
||||
//! Tests whether the label `id` is valid (i.e. registered).
|
||||
ASMJIT_API bool isLabelValid(uint32_t labelId) const noexcept;
|
||||
//! Tests whether the `label` is valid (i.e. registered).
|
||||
inline bool isLabelValid(const Label& label) const noexcept { return isLabelValid(label.id()); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Emit
|
||||
//! \{
|
||||
|
||||
// NOTE: These `emit()` helpers are designed to address a code-bloat generated by C++ compilers to call a function
|
||||
// having many arguments. Each parameter to `_emit()` requires some code to pass it, which means that if we default
|
||||
// to 5 arguments in `_emit()` and instId the C++ compiler would have to generate a virtual function call having 5
|
||||
// parameters and additional `this` argument, which is quite a lot. Since by default most instructions have 2 to 3
|
||||
// operands it's better to introduce helpers that pass from 0 to 6 operands that help to reduce the size of emit(...)
|
||||
// function call.
|
||||
|
||||
//! Emits an instruction (internal).
|
||||
ASMJIT_API Error _emitI(InstId instId);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(InstId instId, const Operand_& o0);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4);
|
||||
//! \overload
|
||||
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`.
|
||||
template<typename... Args>
|
||||
ASMJIT_FORCE_INLINE Error emit(InstId instId, Args&&... operands) {
|
||||
return _emitI(instId, Support::ForwardOp<Args>::forward(operands)...);
|
||||
}
|
||||
|
||||
ASMJIT_FORCE_INLINE Error emitOpArray(InstId instId, const Operand_* operands, size_t opCount) {
|
||||
return _emitOpArray(instId, operands, opCount);
|
||||
}
|
||||
|
||||
ASMJIT_FORCE_INLINE Error emitInst(const BaseInst& inst, const Operand_* operands, size_t opCount) {
|
||||
setInstOptions(inst.options());
|
||||
setExtraReg(inst.extraReg());
|
||||
return _emitOpArray(inst.id(), operands, opCount);
|
||||
}
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! Emits an instruction - all 6 operands must be defined.
|
||||
virtual Error _emit(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* oExt) = 0;
|
||||
//! Emits instruction having operands stored in array.
|
||||
ASMJIT_API virtual Error _emitOpArray(InstId instId, const Operand_* operands, size_t opCount);
|
||||
//! \endcond
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Emit Utilities
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error emitProlog(const FuncFrame& frame);
|
||||
ASMJIT_API Error emitEpilog(const FuncFrame& frame);
|
||||
ASMJIT_API Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Align
|
||||
//! \{
|
||||
|
||||
//! Aligns the current CodeBuffer position to the `alignment` specified.
|
||||
//!
|
||||
//! The sequence that is used to fill the gap between the aligned location and the current location depends on the
|
||||
//! align `mode`, see \ref AlignMode. The `alignment` argument specifies alignment in bytes, so for example when
|
||||
//! it's `32` it means that the code buffer will be aligned to `32` bytes.
|
||||
virtual Error align(AlignMode alignMode, uint32_t alignment) = 0;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Embed
|
||||
//! \{
|
||||
|
||||
//! Embeds raw data into the \ref CodeBuffer.
|
||||
virtual Error embed(const void* data, size_t dataSize) = 0;
|
||||
|
||||
//! Embeds a typed data array.
|
||||
//!
|
||||
//! This is the most flexible function for embedding data as it allows to:
|
||||
//!
|
||||
//! - Assign a `typeId` to the data, so the emitter knows the type of items stored in `data`. Binary data should
|
||||
//! use \ref TypeId::kUInt8.
|
||||
//!
|
||||
//! - Repeat the given data `repeatCount` times, so the data can be used as a fill pattern for example, or as a
|
||||
//! pattern used by SIMD instructions.
|
||||
virtual Error embedDataArray(TypeId typeId, const void* data, size_t itemCount, size_t repeatCount = 1) = 0;
|
||||
|
||||
//! Embeds int8_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt8(int8_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt8, &value, 1, repeatCount); }
|
||||
//! Embeds uint8_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt8(uint8_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt8, &value, 1, repeatCount); }
|
||||
//! Embeds int16_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt16(int16_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt16, &value, 1, repeatCount); }
|
||||
//! Embeds uint16_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt16(uint16_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt16, &value, 1, repeatCount); }
|
||||
//! Embeds int32_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt32(int32_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt32, &value, 1, repeatCount); }
|
||||
//! Embeds uint32_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt32(uint32_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt32, &value, 1, repeatCount); }
|
||||
//! Embeds int64_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt64(int64_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt64, &value, 1, repeatCount); }
|
||||
//! Embeds uint64_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt64(uint64_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt64, &value, 1, repeatCount); }
|
||||
//! Embeds a floating point `value` repeated by `repeatCount`.
|
||||
inline Error embedFloat(float value, size_t repeatCount = 1) { return embedDataArray(TypeId(TypeUtils::TypeIdOfT<float>::kTypeId), &value, 1, repeatCount); }
|
||||
//! Embeds a floating point `value` repeated by `repeatCount`.
|
||||
inline Error embedDouble(double value, size_t repeatCount = 1) { return embedDataArray(TypeId(TypeUtils::TypeIdOfT<double>::kTypeId), &value, 1, repeatCount); }
|
||||
|
||||
//! Embeds a constant pool at the current offset by performing the following:
|
||||
//! 1. Aligns by using AlignMode::kData to the minimum `pool` alignment.
|
||||
//! 2. Binds the ConstPool label so it's bound to an aligned location.
|
||||
//! 3. Emits ConstPool content.
|
||||
virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0;
|
||||
|
||||
//! Embeds an absolute `label` address as data.
|
||||
//!
|
||||
//! The `dataSize` is an optional argument that can be used to specify the size of the address data. If it's zero
|
||||
//! (default) the address size is deduced from the target architecture (either 4 or 8 bytes).
|
||||
virtual Error embedLabel(const Label& label, size_t dataSize = 0) = 0;
|
||||
|
||||
//! Embeds a delta (distance) between the `label` and `base` calculating it as `label - base`. This function was
|
||||
//! designed to make it easier to embed lookup tables where each index is a relative distance of two labels.
|
||||
virtual Error embedLabelDelta(const Label& label, const Label& base, size_t dataSize = 0) = 0;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Comment
|
||||
//! \{
|
||||
|
||||
//! Emits a comment stored in `data` with an optional `size` parameter.
|
||||
virtual Error comment(const char* data, size_t size = SIZE_MAX) = 0;
|
||||
|
||||
//! Emits a formatted comment specified by `fmt` and variable number of arguments.
|
||||
ASMJIT_API Error commentf(const char* fmt, ...);
|
||||
//! Emits a formatted comment specified by `fmt` and `ap`.
|
||||
ASMJIT_API Error commentv(const char* fmt, va_list ap);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
//! Called after the emitter was attached to `CodeHolder`.
|
||||
virtual Error onAttach(CodeHolder* ASMJIT_NONNULL(code)) noexcept = 0;
|
||||
//! Called after the emitter was detached from `CodeHolder`.
|
||||
virtual Error onDetach(CodeHolder* ASMJIT_NONNULL(code)) noexcept = 0;
|
||||
|
||||
//! Called when \ref CodeHolder has updated an important setting, which involves the following:
|
||||
//!
|
||||
//! - \ref Logger has been changed (\ref CodeHolder::setLogger() has been called).
|
||||
//!
|
||||
//! - \ref ErrorHandler has been changed (\ref CodeHolder::setErrorHandler() has been called).
|
||||
//!
|
||||
//! This function ensures that the settings are properly propagated from \ref CodeHolder to the emitter.
|
||||
//!
|
||||
//! \note This function is virtual and can be overridden, however, if you do so, always call \ref
|
||||
//! BaseEmitter::onSettingsUpdated() within your own implementation to ensure that the emitter is
|
||||
//! in a consistent state.
|
||||
ASMJIT_API virtual void onSettingsUpdated() noexcept;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_EMITTER_H_INCLUDED
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user