Commit 61d5cc0f authored by Peter's avatar Peter
Browse files

Merge branch 'master' into applecl

parents e2999354 afae4bc8
...@@ -434,7 +434,7 @@ To download and unpack OpenMM source code: ...@@ -434,7 +434,7 @@ To download and unpack OpenMM source code:
Alternatively, if you want the most recent development version of the code rather Alternatively, if you want the most recent development version of the code rather
than the version corresponding to a particular release, you can get it from than the version corresponding to a particular release, you can get it from
https://github.com/SimTk/openmm. Be aware that the development code is constantly https://github.com/pandegroup/openmm. Be aware that the development code is constantly
changing, may contain bugs, and should never be used for production work. If changing, may contain bugs, and should never be used for production work. If
you want a stable, well tested version of OpenMM, you should download the source you want a stable, well tested version of OpenMM, you should download the source
code for the latest release as described above. code for the latest release as described above.
......
This diff is collapsed.
...@@ -489,12 +489,12 @@ The surface area term is given by\ :cite:`Schaefer1998`\ :cite:`Ponder` ...@@ -489,12 +489,12 @@ The surface area term is given by\ :cite:`Schaefer1998`\ :cite:`Ponder`
.. math:: .. math::
E=4\pi \cdot 2\text{.}\text{26}\sum _{i}{\left({r}_{i}+{r}_{\mathit{solvent}}\right)}^{2}{\left(\frac{{r}_{i}}{{R}_{i}}\right)}^{6} E=E_{SA} \cdot 4\pi \sum _{i}{\left({r}_{i}+{r}_{\mathit{solvent}}\right)}^{2}{\left(\frac{{r}_{i}}{{R}_{i}}\right)}^{6}
where :math:`r_i` is the atomic radius of particle *i*\ , :math:`r_i` is where :math:`r_i` is the atomic radius of particle *i*\ , :math:`r_i` is
its Born radius, and :math:`r_\mathit{solvent}` is the solvent radius, which is taken its atomic radius, and :math:`r_\mathit{solvent}` is the solvent radius, which is taken
to be 0.14 nm. to be 0.14 nm. The default value for the energy scale :math:`E_{SA}` is 2.25936 kJ/mol/nm\ :sup:`2`\ .
AndersenThermostat AndersenThermostat
...@@ -534,18 +534,18 @@ of the periodic box to vary with time.\ :cite:`Chow1995`\ :cite:`Aqvist2004` ...@@ -534,18 +534,18 @@ of the periodic box to vary with time.\ :cite:`Chow1995`\ :cite:`Aqvist2004`
At regular intervals, it attempts a Monte Carlo step by scaling the box vectors At regular intervals, it attempts a Monte Carlo step by scaling the box vectors
and the coordinates of each molecule’s center by a factor *s*\ . The scale and the coordinates of each molecule’s center by a factor *s*\ . The scale
factor *s* is chosen to change the volume of the periodic box from *V* factor *s* is chosen to change the volume of the periodic box from *V*
to *V*\ +\ :math:`\delta`\ *V*\ : to *V*\ +\ :math:`\Delta`\ *V*\ :
.. math:: .. math::
s={\left(\frac{V+\delta V}{V}\right)}^{1/3} s={\left(\frac{V+\Delta V}{V}\right)}^{1/3}
The change in volume is chosen randomly as The change in volume is chosen randomly as
.. math:: .. math::
\delta V=A\cdot r \Delta V=A\cdot r
where *A* is a scale factor and *r* is a random number uniformly where *A* is a scale factor and *r* is a random number uniformly
...@@ -554,7 +554,7 @@ weight function ...@@ -554,7 +554,7 @@ weight function
.. math:: .. math::
\Delta W=\Delta E+P\delta V-Nk_{B}T \text{ln}\left(\frac{V+\delta V}{V}\right) \Delta W=\Delta E+P\Delta V-Nk_{B}T \text{ln}\left(\frac{V+\Delta V}{V}\right)
where :math:`\Delta E` is the change in potential energy resulting from the step, where :math:`\Delta E` is the change in potential energy resulting from the step,
...@@ -602,6 +602,39 @@ You can specify that the barostat should only be applied to certain axes of the ...@@ -602,6 +602,39 @@ You can specify that the barostat should only be applied to certain axes of the
box, keeping the other axes fixed. This is useful, for example, when doing box, keeping the other axes fixed. This is useful, for example, when doing
constant surface area simulations of membranes. constant surface area simulations of membranes.
MonteCarloMembraneBarostat
**************************
MonteCarloMembraneBarostat is very similar to MonteCarloBarostat, but it is
specialized for simulations of membranes. It assumes the membrane lies in the
XY plane. In addition to applying a uniform pressure to regulate the volume of
the periodic box, it also applies a uniform surface tension to regulate the
cross sectional area of the periodic box in the XY plane. The weight function
for deciding whether to accept a step is
.. math::
\Delta W=\Delta E+P\Delta V-S\Delta A-Nk_{B}T \text{ln}\left(\frac{V+\Delta V}{V}\right)
where *S* is the surface tension and :math:`\Delta`\ *A* is the change in cross
sectional area. Notice that pressure and surface tension are defined with
opposite senses: a larger pressure tends to make the box smaller, but a larger
surface tension tends to make the box larger.
MonteCarloMembraneBarostat offers some additional options to customize the
behavior of the periodic box:
* The X and Y axes can be either
* isotropic (they are always scaled by the same amount, so their ratio remains fixed)
* anisotropic (they can change size independently)
* The Z axis can be either
* free (its size changes independently of the X and Y axes)
* fixed (its size does not change)
* inversely varying with the X and Y axes (so the total box volume does not
change)
CMMotionRemover CMMotionRemover
*************** ***************
...@@ -1007,7 +1040,7 @@ The following operators are supported: + (add), - (subtract), * (multiply), / ...@@ -1007,7 +1040,7 @@ The following operators are supported: + (add), - (subtract), * (multiply), /
The following standard functions are supported: sqrt, exp, log, sin, cos, sec, The following standard functions are supported: sqrt, exp, log, sin, cos, sec,
csc, tan, cot, asin, acos, atan, sinh, cosh, tanh, erf, erfc, min, max, abs, csc, tan, cot, asin, acos, atan, sinh, cosh, tanh, erf, erfc, min, max, abs,
step, delta. step(x) = 0 if x < 0, 1 otherwise. delta(x) = 1 if x is 0, 0 floor, ceil, step, delta. step(x) = 0 if x < 0, 1 otherwise. delta(x) = 1 if x is 0, 0
otherwise. Some custom forces allow additional functions to be defined from otherwise. Some custom forces allow additional functions to be defined from
tabulated values. tabulated values.
...@@ -1228,6 +1261,61 @@ Other Features ...@@ -1228,6 +1261,61 @@ Other Features
############## ##############
Periodic Boundary Conditions
****************************
Many Force objects support periodic boundary conditions. They act as if space
were tiled with infinitely repeating copies of the system, then compute the
forces acting on a single copy based on the infinite periodic copies. In most
(but not all) cases, they apply a cutoff so that each particle only interacts
with a single copy of each other particle.
OpenMM supports triclinic periodic boxes. This means the periodicity is defined
by three vectors, :math:`\mathbf{a}`\ , :math:`\mathbf{b}`\ , and
:math:`\mathbf{c}`\ . Given a particle position, the infinite periodic copies
of that particle are generated by adding vectors of the form
:math:`i \mathbf{a}+j \mathbf{b}+k \mathbf{c}`\ , where :math:`i`\ ,
:math:`j`\ , and :math:`k` are arbitrary integers.
The periodic box vectors must be chosen to satisfy certain requirements.
Roughly speaking, :math:`\mathbf{a}`\ , :math:`\mathbf{b}`\ , and
:math:`\mathbf{c}` need to "mostly" correspond to the x, y, and z axes. They
must have the form
.. math::
\mathbf{a} = (a_x, 0, 0)
\mathbf{b} = (b_x, b_y, 0)
\mathbf{c} = (c_x, c_y, c_z)
It is always possible to put the box vectors into this form by rotating the
system until :math:`\mathbf{a}` is parallel to x and :math:`\mathbf{b}` lies in
the xy plane.
Furthermore, they must obey the following constraints:
.. math::
a_x > 0, b_y > 0, c_z > 0
a_x \ge 2 |b_x|
a_x \ge 2 |c_x|
b_y \ge 2 |c_y|
This effectively requires the box vectors to be specified in a particular
reduced form. By forming combinations of box vectors (a process known as
"lattice reduction"), it is always possible to put them in this form without
changing the periodic system they represent.
These requirements have an important consequence: the periodic unit cell can
always be treated as an axis-aligned rectangular box of size
:math:`(a_x, b_y, c_z)`\ . The remaining non-zero elements of the box vectors
cause the repeating copies of the system to be staggered relative to each other,
but they do not affect the shape or size of each copy. The volume of the unit
cell is simply given by :math:`a_x b_y c_z`\ .
LocalEnergyMinimizer LocalEnergyMinimizer
******************** ********************
...@@ -1346,3 +1434,67 @@ specific types of rules. They are: ...@@ -1346,3 +1434,67 @@ specific types of rules. They are:
.. math:: .. math::
\mathbf{r}=\mathbf{o}+p_1\mathbf{\hat{x}}+p_2\mathbf{\hat{y}}+p_3\mathbf{\hat{z}} \mathbf{r}=\mathbf{o}+p_1\mathbf{\hat{x}}+p_2\mathbf{\hat{y}}+p_3\mathbf{\hat{z}}
.. ..
Random Numbers with Stochastic Integrators and Forces
*****************************************************
OpenMM includes many stochastic integrators and forces that make extensive use
of random numbers. It is impossible to generate truly random numbers on a
computer like you would with a dice roll or coin flip in real life---instead
programs rely on pseudo-random number generators (PRNGs) that take some sort of
initial "seed" value and steps through a sequence of seemingly random numbers.
The exact implementation of the PRNGs is not important (in fact, each platform
may have its own PRNG whose performance is optimized for that hardware). What
*is* important, however, is that the PRNG must generate a uniform distribution
of random numbers between 0 and 1. Random numbers drawn from this distribution
can be manipulated to yield random integers in a desired range or even a random
number from a different type of probability distribution function (e.g., a
normal distribution).
What this means is that the random numbers used by integrators and forces within
OpenMM cannot have any discernible pattern to them. Patterns can be induced in
PRNGs in two principal ways:
1. The PRNG uses a bad algorithm with a short period.
2. Two PRNGs are started using the same seed
All PRNG algorithms in common use are periodic---that is their sequence of
random numbers repeats after a given *period*, defined by the number of "unique"
random numbers in the repeating pattern. As long as this period is longer than
the total number of random numbers your application requires (preferably by
orders of magnitude), the first problem described above is avoided. All PRNGs
employed by OpenMM have periods far longer than any current simulation can cycle
through.
Point two is far more common in biomolecular simulation, and can result in very
strange artifacts that may be difficult to detect. For example, with Langevin
dynamics, two simulations that use the same sequence of random numbers appear to
synchronize in their global movements.\ :cite:`Uberuaga2004`\
:cite:`Sindhikara2009` It is therefore very important that the stochastic forces
and integrators in OpenMM generate unique sequences of pseudo-random numbers not
only within a single simulation, but between two different simulations of the
same system as well (including any restarts of previous simulations).
Every stochastic force and integrator that does (or could) make use of random
numbers has two instance methods attached to it: :meth:`getRandomNumberSeed()`
and :meth:`setRandomNumberSeed(int seed)`. If you set a unique random seed for
two different simulations (or different forces/integrators if applicable),
OpenMM guarantees that the generated sequences of random numbers will be
different (by contrast, no guarantee is made that the same seed will result in
identical random number sequences).
Since breaking simulations up into pieces and/or running multiple replicates of
a system to obtain more complete statistics is common practice, a new strategy
has been employed for OpenMM versions 6.3 and later with the aim of trying to
ensure that each simulation will be started with a unique random seed. A random
seed value of 0 (the default) will cause a unique random seed to be generated
when a new :class:`Context` is instantiated.
Prior to the introduction of this feature, deserializing a serialized
:class:`System` XML file would result in each stochastic force or integrator
being assigned the same random seed as the original instance that was
serialized. If you use a :class:`System` XML file generated by a version of
OpenMM older than 6.3 to start a new simulation, you should manually set the
random number seed of each stochastic force or integrator to 0 (or another
unique value).
...@@ -93,7 +93,7 @@ FOREACH(EX_ROOT ${F_EXAMPLES}) ...@@ -93,7 +93,7 @@ FOREACH(EX_ROOT ${F_EXAMPLES})
INSTALL(FILES ${EX_ROOT}.f90 DESTINATION examples) INSTALL(FILES ${EX_ROOT}.f90 DESTINATION examples)
ENDFOREACH(EX_ROOT ${F_EXAMPLES}) ENDFOREACH(EX_ROOT ${F_EXAMPLES})
INSTALL(FILES simulateAmber.py simulatePdb.py simulateGromacs.py testInstallation.py argon-chemical-potential.py input.inpcrd input.prmtop input.pdb input.gro input.top INSTALL(FILES simulateAmber.py simulatePdb.py simulateGromacs.py benchmark.py argon-chemical-potential.py input.inpcrd input.prmtop input.pdb input.gro input.top 5dfr_minimized.pdb 5dfr_solv-cube_equil.pdb
DESTINATION examples) DESTINATION examples)
INSTALL(FILES VisualStudio/HelloArgon.vcproj INSTALL(FILES VisualStudio/HelloArgon.vcproj
......
...@@ -6,9 +6,9 @@ import sys ...@@ -6,9 +6,9 @@ import sys
from datetime import datetime from datetime import datetime
from optparse import OptionParser from optparse import OptionParser
def timeIntegration(context, steps): def timeIntegration(context, steps, initialSteps):
"""Integrate a Context for a specified number of steps, then return how many seconds it took.""" """Integrate a Context for a specified number of steps, then return how many seconds it took."""
context.getIntegrator().step(5) # Make sure everything is fully initialized context.getIntegrator().step(initialSteps) # Make sure everything is fully initialized
context.getState(getEnergy=True) context.getState(getEnergy=True)
start = datetime.now() start = datetime.now()
context.getIntegrator().step(steps) context.getIntegrator().step(steps)
...@@ -79,11 +79,14 @@ def runOneTest(testName, options): ...@@ -79,11 +79,14 @@ def runOneTest(testName, options):
system = ff.createSystem(pdb.topology, nonbondedMethod=method, nonbondedCutoff=cutoff, constraints=constraints, hydrogenMass=hydrogenMass) system = ff.createSystem(pdb.topology, nonbondedMethod=method, nonbondedCutoff=cutoff, constraints=constraints, hydrogenMass=hydrogenMass)
print('Step Size: %g fs' % dt.value_in_unit(unit.femtoseconds)) print('Step Size: %g fs' % dt.value_in_unit(unit.femtoseconds))
properties = {} properties = {}
initialSteps = 5
if options.device is not None: if options.device is not None:
if platform.getName() == 'CUDA': if platform.getName() == 'CUDA':
properties['CudaDeviceIndex'] = options.device properties['CudaDeviceIndex'] = options.device
elif platform.getName() == 'OpenCL': elif platform.getName() == 'OpenCL':
properties['OpenCLDeviceIndex'] = options.device properties['OpenCLDeviceIndex'] = options.device
if ',' in options.device or ' ' in options.device:
initialSteps = 250
if options.precision is not None: if options.precision is not None:
if platform.getName() == 'CUDA': if platform.getName() == 'CUDA':
properties['CudaPrecision'] = options.precision properties['CudaPrecision'] = options.precision
...@@ -102,7 +105,7 @@ def runOneTest(testName, options): ...@@ -102,7 +105,7 @@ def runOneTest(testName, options):
context.setVelocitiesToTemperature(300*unit.kelvin) context.setVelocitiesToTemperature(300*unit.kelvin)
steps = 20 steps = 20
while True: while True:
time = timeIntegration(context, steps) time = timeIntegration(context, steps, initialSteps)
if time >= 0.5*options.seconds: if time >= 0.5*options.seconds:
break break
if time < 0.5: if time < 0.5:
......
...@@ -4,7 +4,7 @@ from simtk.unit import * ...@@ -4,7 +4,7 @@ from simtk.unit import *
from sys import stdout from sys import stdout
gro = GromacsGroFile('input.gro') gro = GromacsGroFile('input.gro')
top = GromacsTopFile('input.top', unitCellDimensions=gro.getUnitCellDimensions(), includeDir='/usr/local/gromacs/share/gromacs/top') top = GromacsTopFile('input.top', periodicBoxVectors=gro.getPeriodicBoxVectors(), includeDir='/usr/local/gromacs/share/gromacs/top')
system = top.createSystem(nonbondedMethod=PME, nonbondedCutoff=1*nanometer, constraints=HBonds) system = top.createSystem(nonbondedMethod=PME, nonbondedCutoff=1*nanometer, constraints=HBonds)
integrator = LangevinIntegrator(300*kelvin, 1/picosecond, 0.002*picoseconds) integrator = LangevinIntegrator(300*kelvin, 1/picosecond, 0.002*picoseconds)
simulation = Simulation(top.topology, system, integrator) simulation = Simulation(top.topology, system, integrator)
......
from __future__ import print_function
# First make sure OpenMM is installed.
import sys
try:
from simtk.openmm.app import *
from simtk.openmm import *
from simtk.unit import *
except ImportError as err:
print("Failed to import OpenMM packages:", err.message)
print("Make sure OpenMM is installed and the library path is set correctly.")
sys.exit()
# Create a System for the tests.
pdb = PDBFile('input.pdb')
forcefield = ForceField('amber99sb.xml', 'tip3p.xml')
system = forcefield.createSystem(pdb.topology, nonbondedMethod=PME, nonbondedCutoff=1*nanometer, constraints=HBonds)
# List all installed platforms and compute forces with each one.
numPlatforms = Platform.getNumPlatforms()
print("There are", numPlatforms, "Platforms available:")
print()
forces = [None]*numPlatforms
for i in range(numPlatforms):
platform = Platform.getPlatform(i)
print(i+1, platform.getName(), end=" ")
integrator = LangevinIntegrator(300*kelvin, 1/picosecond, 0.002*picoseconds)
try:
simulation = Simulation(pdb.topology, system, integrator, platform)
simulation.context.setPositions(pdb.positions)
forces[i] = simulation.context.getState(getForces=True).getForces()
del simulation
print("- Successfully computed forces")
except:
print("- Error computing forces with", platform.getName(), "platform")
# See how well the platforms agree.
if numPlatforms > 1:
print()
print("Median difference in forces between platforms:")
print()
for i in range(numPlatforms):
for j in range(i):
if forces[i] is not None and forces[j] is not None:
errors = []
for f1, f2 in zip(forces[i], forces[j]):
d = f1-f2
error = sqrt((d[0]*d[0]+d[1]*d[1]+d[2]*d[2])/(f1[0]*f1[0]+f1[1]*f1[1]+f1[2]*f1[2]))
errors.append(error)
print("{} vs. {}: {:g}".format(Platform.getPlatform(j).getName(),
Platform.getPlatform(i).getName(),
sorted(errors)[len(errors)//2]))
#!/bin/sh
cd $(dirname $0)
# Ask the user for the install location and Python executable.
defaultInstallDir=/usr/local/openmm
printf "Enter install location (default=${defaultInstallDir}): "
read installDir
if [ -z ${installDir} ]
then
installDir=${defaultInstallDir}
fi
defaultPythonBin=$(which python)
printf "Enter path to Python executable"
if [ ${defaultPythonBin} ]
then
printf " (default=${defaultPythonBin})"
fi
printf ": "
read pythonBin
if [ -z ${pythonBin} ]
then
pythonBin=${defaultPythonBin}
fi
# Make sure it's a supported Python version.
pythonOk=$(${pythonBin} -c "import sys; v=sys.version_info; print((v[0]==2 and v[1]>5) or v[0]>2)")
if [ ${pythonOk} != "True" ]
then
echo "Unsupported Python version. Only versions 2.6 and higher are supported."
exit
fi
# Copy the files into place.
cp -R docs ${installDir}
cp -R include ${installDir}
cp -R lib ${installDir}
cp -R licenses ${installDir}
# Run the Python installer.
cd python
export OPENMM_INCLUDE_PATH=${installDir}/include
export OPENMM_LIB_PATH=${installDir}/lib
printenv
if ${pythonBin} setup.py build && ${pythonBin} setup.py install $@
then
# Print instructions to the user.
echo
echo "Installation is complete. You should now test your installation to make sure"
echo "it is working correctly by typing the following command:"
echo
echo "python -m simtk.testInstallation"
else
echo
echo "INSTALLATION FAILED"
echo
echo "An error prevented the installation from completing. See above for details."
fi
AsmJit - Complete x86/x64 JIT and Remote Assembler for C++
Copyright (c) 2008-2014, Petr Kobalicek <kobalicek.petr@gmail.com>
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.
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Dependencies - AsmJit]
#if !defined(_ASMJIT_BUILD_H)
#include "build.h"
#endif // !_ASMJIT_BUILD_H
// [Guard]
#if !defined(ASMJIT_API_SCOPE)
# define ASMJIT_API_SCOPE
#else
# error "AsmJit - Api-Scope is already active, previous scope not closed by apiend.h?"
#endif // ASMJIT_API_SCOPE
// ============================================================================
// [Override]
// ============================================================================
#if !defined(ASMJIT_CC_HAS_OVERRIDE) && !defined(override)
# define override
# define ASMJIT_UNDEF_OVERRIDE
#endif // !ASMJIT_CC_HAS_OVERRIDE && !override
// ============================================================================
// [NoExcept]
// ============================================================================
#if !defined(ASMJIT_CC_HAS_NOEXCEPT) && !defined(noexcept)
# define noexcept ASMJIT_NOEXCEPT
# define ASMJIT_UNDEF_NOEXCEPT
#endif // !ASMJIT_CC_HAS_NOEXCEPT && !noexcept
// ============================================================================
// [MSC]
// ============================================================================
#if defined(_MSC_VER)
// Disable some warnings we know about
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4201) // nameless struct/union
# pragma warning(disable: 4244) // '+=' : conversion from 'int' to 'x', possible
// loss of data
# pragma warning(disable: 4251) // struct needs to have dll-interface to be used
// by clients of struct ...
# pragma warning(disable: 4275) // non dll-interface struct ... used as base for
// dll-interface struct
# pragma warning(disable: 4355) // this used in base member initializer list
# pragma warning(disable: 4480) // specifying underlying type for enum
# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false'
// Rename symbols.
# if !defined(vsnprintf)
# define ASMJIT_UNDEF_VSNPRINTF
# define vsnprintf _vsnprintf
# endif // !vsnprintf
# if !defined(snprintf)
# define ASMJIT_UNDEF_SNPRINTF
# define snprintf _snprintf
# endif // !snprintf
#endif // _MSC_VER
// ============================================================================
// [CLang]
// ============================================================================
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunnamed-type-template-args"
#endif // __clang__
// ============================================================================
// [GCC]
// ============================================================================
#if defined(__GNUC__) && !defined(__clang__)
# if __GNUC__ >= 4 && !defined(__MINGW32__)
# pragma GCC visibility push(hidden)
# endif // GCC 4+
#endif // __GNUC__
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#if defined(ASMJIT_API_SCOPE)
# undef ASMJIT_API_SCOPE
#else
# error "AsmJit - Api-Scope not active, forgot to include apibegin.h?"
#endif // ASMJIT_API_SCOPE
// ============================================================================
// [Override]
// ============================================================================
#if defined(ASMJIT_UNDEF_OVERRIDE)
# undef override
# undef ASMJIT_UNDEF_OVERRIDE
#endif // ASMJIT_UNDEF_OVERRIDE
// ============================================================================
// [NoExcept]
// ============================================================================
#if defined(ASMJIT_UNDEF_NOEXCEPT)
# undef noexcept
# undef ASMJIT_UNDEF_NOEXCEPT
#endif // ASMJIT_UNDEF_NOEXCEPT
// ============================================================================
// [MSC]
// ============================================================================
#if defined(_MSC_VER)
# pragma warning(pop)
# if defined(ASMJIT_UNDEF_VSNPRINTF)
# undef vsnprintf
# undef ASMJIT_UNDEF_VSNPRINTF
# endif // ASMJIT_UNDEF_VSNPRINTF
# if defined(ASMJIT_UNDEF_SNPRINTF)
# undef snprintf
# undef ASMJIT_UNDEF_SNPRINTF
# endif // ASMJIT_UNDEF_SNPRINTF
#endif // _MSC_VER
// ============================================================================
// [CLang]
// ============================================================================
#if defined(__clang__)
# pragma clang diagnostic pop
#endif // __clang__
// ============================================================================
// [GCC]
// ============================================================================
#if defined(__GNUC__) && !defined(__clang__)
# if __GNUC__ >= 4 && !defined(__MINGW32__)
# pragma GCC visibility pop
# endif // GCC 4+
#endif // __GNUC__
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_ASMJIT_H
#define _ASMJIT_ASMJIT_H
// ============================================================================
// [asmjit_mainpage]
// ============================================================================
//! @mainpage
//!
//! AsmJit - Complete x86/x64 JIT and Remote Assembler for C++.
//!
//! AsmJit is a complete JIT and remote assembler for C++ language. It can
//! generate native code for x86 and x64 architectures having support for
//! a full instruction set, from legacy MMX to the newest AVX2. It has a
//! type-safe API that allows C++ compiler to do a semantic checks at
//! compile-time even before the assembled code is generated or run.
//!
//! AsmJit is not a virtual machine (VM). It doesn't have functionality to
//! implement VM out of the box; however, it can be be used as a JIT backend
//! for your own VM. The usage of AsmJit is not limited at all; it's suitable
//! for multimedia, VM backends or remote code generation.
//!
//! @section AsmJit_Concepts Code Generation Concepts
//!
//! AsmJit has two completely different code generation concepts. The difference
//! is in how the code is generated. The first concept, also referred as the low
//! level concept, is called 'Assembler' and it's the same as writing RAW
//! assembly by using physical registers directly. In this case AsmJit does only
//! instruction encoding, verification and relocation.
//!
//! The second concept, also referred as the high level concept, is called
//! 'Compiler'. Compiler lets you use virtually unlimited number of registers
//! (called variables) significantly simplifying the code generation process.
//! Compiler allocates these virtual registers to physical registers after the
//! code generation is done. This requires some extra effort - Compiler has to
//! generate information for each node (instruction, function declaration,
//! function call) in the code, perform a variable liveness analysis and
//! translate the code having variables into code having only registers.
//!
//! In addition, Compiler understands functions and function calling conventions.
//! It has been designed in a way that the code generated is always a function
//! having prototype like in a programming language. By having a function
//! prototype the Compiler is able to insert prolog and epilog to a function
//! being generated and it is able to call a function inside a generated one.
//!
//! There is no conclusion on which concept is better. Assembler brings full
//! control on how the code is generated, while Compiler makes the generation
//! more portable.
//!
//! @section AsmJit_Main_CodeGeneration Code Generation
//!
//! - \ref asmjit_base_general "Assembler core" - Operands, intrinsics and low-level assembler.
//! - \ref asmjit_compiler "Compiler" - High level code generation.
//! - \ref asmjit_cpuinfo "Cpu Information" - Get information about host processor.
//! - \ref asmjit_logging "Logging" - Logging and error handling.
//! - \ref AsmJit_MemoryManagement "Memory Management" - Virtual memory management.
//!
//! @section AsmJit_Main_HomePage AsmJit Homepage
//!
//! - https://github.com/kobalicek/asmjit
// ============================================================================
// [asmjit_base]
// ============================================================================
//! \defgroup asmjit_base AsmJit
//!
//! \brief AsmJit.
// ============================================================================
// [asmjit_base_general]
// ============================================================================
//! \defgroup asmjit_base_general AsmJit General API
//! \ingroup asmjit_base
//!
//! \brief AsmJit general API.
//!
//! Contains all `asmjit` classes and helper functions that are architecture
//! independent or abstract. Abstract classes are implemented by the backend,
//! for example `Assembler` is implemented by `X86Assembler`.
//!
//! - See `Assembler` for low level code generation documentation.
//! - See `Compiler` for high level code generation documentation.
//! - See `Operand` for operand's overview.
//!
//! Logging and Error Handling
//! --------------------------
//!
//! AsmJit contains robust interface that can be used to log the generated code
//! and to handle possible errors. Base logging interface is defined in `Logger`
//! class that is abstract and can be overridden. AsmJit contains two loggers
//! that can be used out of the box - `FileLogger` that logs into a pure C
//! `FILE*` stream and `StringLogger` that just concatenates all log messages
//! by using a `StringBuilder` class.
//!
//! The following snippet shows how to setup a logger that logs to `stderr`:
//!
//! ~~~
//! // `FileLogger` instance.
//! FileLogger logger(stderr);
//!
//! // `Compiler` or any other `CodeGen` interface.
//! host::Compiler c;
//!
//! // use `setLogger` to replace the `CodeGen` logger.
//! c.setLogger(&logger);
//! ~~~
//!
//! \sa \ref Logger, \ref FileLogger, \ref StringLogger.
// ============================================================================
// [asmjit_base_compiler]
// ============================================================================
//! \defgroup asmjit_base_compiler AsmJit Compiler
//! \ingroup asmjit_base
//!
//! \brief AsmJit code-tree used by Compiler.
//!
//! AsmJit intermediate code-tree is a double-linked list that is made of nodes
//! that represent assembler instructions, directives, labels and high-level
//! constructs compiler is using to represent functions and function calls. The
//! node list can only be used together with \ref Compiler.
//!
//! TODO
// ============================================================================
// [asmjit_base_util]
// ============================================================================
//! \defgroup asmjit_base_util AsmJit Utilities
//! \ingroup asmjit_base
//!
//! \brief AsmJit utility classes.
//!
//! AsmJit contains numerous utility classes that are needed by the library
//! itself. The most useful ones have been made public and are now exported.
//!
//! POD Containers
//! --------------
//!
//! POD containers are used by AsmJit to manage its own data structures. The
//! following classes can be used by AsmJit consumers:
//!
//! - \ref PodVector - Simple growing array-like container for POD data.
//! - \ref StringBuilder - Simple string builder that can append string
//! and integers.
//!
//! Zone Memory Allocator
//! ---------------------
//!
//! Zone memory allocator is an incremental memory allocator that can be used
//! to allocate data of short life-time. It has much better performance
//! characteristics than all other allocators, because the only thing it can do
//! is to increment a pointer and return its previous address. See \ref Zone
//! for more details.
//!
//! CPU Ticks
//! ---------
//!
//! CPU Ticks is a simple helper that can be used to do basic benchmarks. See
//! \ref CpuTicks class for more details.
//!
//! Integer Utilities
//! -----------------
//!
//! Integer utilities are all implemented by a static class \ref IntUtil.
//! There are utilities for bit manipulation and bit counting, utilities to get
//! an integer minimum / maximum and various other helpers required to perform
//! alignment checks and binary casting from float to integer and vica versa.
//!
//! Vector Utilities
//! ----------------
//!
//! SIMD code generation often requires to embed constants after each function
//! or a block of functions generated. AsmJit contains classes `Vec64`,
//! `Vec128` and `Vec256` that can be used to prepare data useful when
//! generating SIMD code.
//!
//! X86/X64 code generator contains member functions `dmm`, `dxmm` and `dymm`
//! which can be used to embed 64-bit, 128-bit and 256-bit data structures into
//! machine code (both assembler and compiler are supported).
//!
//! \note Compiler contains a constant pool, which should be used instead of
//! embedding constants manually after the function body.
// ============================================================================
// [asmjit_x86]
// ============================================================================
//! \defgroup asmjit_x86 X86/X64
//!
//! \brief X86/X64 module
// ============================================================================
// [asmjit_x86_general]
// ============================================================================
//! \defgroup asmjit_x86_general X86/X64 General API
//! \ingroup asmjit_x86
//!
//! \brief X86/X64 general API.
//!
//! X86/X64 Registers
//! -----------------
//!
//! There are static objects that represents X86 and X64 registers. They can
//! be used directly (like `eax`, `mm`, `xmm`, ...) or created through
//! these functions:
//!
//! - `asmjit::gpb_lo()` - Get Gpb-lo register.
//! - `asmjit::gpb_hi()` - Get Gpb-hi register.
//! - `asmjit::gpw()` - Get Gpw register.
//! - `asmjit::gpd()` - Get Gpd register.
//! - `asmjit::gpq()` - Get Gpq Gp register.
//! - `asmjit::gpz()` - Get Gpd/Gpq register.
//! - `asmjit::fp()` - Get Fp register.
//! - `asmjit::mm()` - Get Mm register.
//! - `asmjit::xmm()` - Get Xmm register.
//! - `asmjit::ymm()` - Get Ymm register.
//!
//! X86/X64 Addressing
//! ------------------
//!
//! X86 and x64 architectures contains several addressing modes and most ones
//! are possible with AsmJit library. Memory represents are represented by
//! `BaseMem` class. These functions are used to make operands that represents
//! memory addresses:
//!
//! - `asmjit::ptr()` - Address size not specified.
//! - `asmjit::byte_ptr()` - 1 byte.
//! - `asmjit::word_ptr()` - 2 bytes (Gpw size).
//! - `asmjit::dword_ptr()` - 4 bytes (Gpd size).
//! - `asmjit::qword_ptr()` - 8 bytes (Gpq/Mm size).
//! - `asmjit::tword_ptr()` - 10 bytes (FPU).
//! - `asmjit::oword_ptr()` - 16 bytes (Xmm size).
//! - `asmjit::yword_ptr()` - 32 bytes (Ymm size).
//! - `asmjit::zword_ptr()` - 64 bytes (Zmm size).
//!
//! Most useful function to make pointer should be `asmjit::ptr()`. It creates
//! pointer to the target with unspecified size. Unspecified size works in all
//! intrinsics where are used registers (this means that size is specified by
//! register operand or by instruction itself). For example `asmjit::ptr()`
//! can't be used with `Assembler::inc()` instruction. In this case size must
//! be specified and it's also reason to make difference between pointer sizes.
//!
//! Supported are simple address forms `[base + displacement]` and complex
//! address forms `[base + index * scale + displacement]`.
//!
//! X86/X64 Immediates
//! ------------------
//!
//! Immediate values are constants thats passed directly after instruction
//! opcode. To create such value use `imm()` or `imm_u()` methods to create
//! signed or unsigned immediate value.
//!
//! X86/X64 CPU Information
//! -----------------------
//!
//! The CPUID instruction can be used to get an exhaustive information about
//! the host X86/X64 processor. AsmJit contains utilities that can get the most
//! important information related to the features supported by the CPU and the
//! host operating system, in addition to host processor name and number of
//! cores. Class `X86CpuInfo` extends `CpuInfo` and provides functionality
//! specific to X86 and X64.
//!
//! By default AsmJit queries the CPU information after the library is loaded
//! and the queried information is reused by all instances of `JitRuntime`.
//! The global instance of `X86CpuInfo` can't be changed, because it will affect
//! the code generation of all `Runtime`s. If there is a need to have a
//! specific CPU information which contains modified features or processor
//! vendor it's possible by creating a new instance of `X86CpuInfo` and setting
//! up its members. `X86CpuUtil::detect` can be used to detect CPU features into
//! an existing `X86CpuInfo` instance - it may become handly if only one property
//! has to be turned on/off.
//!
//! If the high-level interface `X86CpuInfo` offers is not enough there is also
//! `X86CpuUtil::callCpuId` helper that can be used to call CPUID instruction
//! with a given parameters and to consume the output.
//!
//! Cpu detection is important when generating a JIT code that may or may not
//! use certain CPU features. For example there used to be a SSE/SSE2 detection
//! in the past and today there is often AVX/AVX2 detection.
//!
//! The example below shows how to detect SSE2:
//!
//! ~~~
//! using namespace asmjit;
//!
//! // Get `X86CpuInfo` global instance.
//! const X86CpuInfo* cpuInfo = X86CpuInfo::getHost();
//!
//! if (cpuInfo->hasFeature(kX86CpuFeatureSSE2)) {
//! // Processor has SSE2.
//! }
//! else if (cpuInfo->hasFeature(kX86CpuFeatureMMX)) {
//! // Processor doesn't have SSE2, but has MMX.
//! }
//! else {
//! // Processor is archaic; it's a wonder AsmJit works here!
//! }
//! ~~~
//!
//! The next example shows how to call `CPUID` directly:
//!
//! ~~~
//! using namespace asmjit;
//!
//! // Call cpuid, first two arguments are passed in Eax/Ecx.
//! X86CpuId out;
//! X86CpuUtil::callCpuId(0, 0, &out);
//!
//! // If Eax argument is 0, Ebx, Ecx and Edx registers are filled with a cpu vendor.
//! char cpuVendor[13];
//! ::memcpy(cpuVendor, &out.ebx, 4);
//! ::memcpy(cpuVendor + 4, &out.edx, 4);
//! ::memcpy(cpuVendor + 8, &out.ecx, 4);
//! vendor[12] = '\0';
//!
//! // Print a CPU vendor retrieved from CPUID.
//! ::printf("%s", cpuVendor);
//! ~~~
// ============================================================================
// [asmjit_x86_compiler]
// ============================================================================
//! \defgroup asmjit_x86_compiler X86/X64 Code-Tree
//! \ingroup asmjit_x86
//!
//! \brief X86/X64 code-tree and helpers.
// ============================================================================
// [asmjit_x86_inst]
// ============================================================================
//! \defgroup asmjit_x86_inst X86/X64 Instructions
//! \ingroup asmjit_x86
//!
//! \brief X86/X64 low-level instruction definitions.
// ============================================================================
// [asmjit_x86_util]
// ============================================================================
//! \defgroup asmjit_x86_util X86/X64 Utilities
//! \ingroup asmjit_x86
//!
//! \brief X86/X64 utility classes.
// ============================================================================
// [asmjit_contrib]
// ============================================================================
//! \defgroup asmjit_contrib Contributions
//!
//! \brief Contributions.
// [Dependencies - Base]
#include "base.h"
// [Dependencies - X86/X64]
#if defined(ASMJIT_BUILD_X86) || defined(ASMJIT_BUILD_X64)
#include "x86.h"
#endif // ASMJIT_BUILD_X86 || ASMJIT_BUILD_X64
// [Dependencies - Host]
#include "host.h"
// [Guard]
#endif // _ASMJIT_ASMJIT_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_H
#define _ASMJIT_BASE_H
// [Dependencies - AsmJit]
#include "build.h"
#include "base/assembler.h"
#include "base/codegen.h"
#include "base/compiler.h"
#include "base/constpool.h"
#include "base/containers.h"
#include "base/cpuinfo.h"
#include "base/cputicks.h"
#include "base/error.h"
#include "base/globals.h"
#include "base/intutil.h"
#include "base/lock.h"
#include "base/logger.h"
#include "base/operand.h"
#include "base/runtime.h"
#include "base/string.h"
#include "base/vectypes.h"
#include "base/vmem.h"
#include "base/zone.h"
// [Guard]
#endif // _ASMJIT_BASE_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/assembler.h"
#include "../base/intutil.h"
#include "../base/vmem.h"
// [Dependenceis - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Assembler - Construction / Destruction]
// ============================================================================
Assembler::Assembler(Runtime* runtime) :
CodeGen(runtime),
_buffer(NULL),
_end(NULL),
_cursor(NULL),
_trampolineSize(0),
_comment(NULL),
_unusedLinks(NULL) {}
Assembler::~Assembler() {
reset(true);
}
// ============================================================================
// [asmjit::Assembler - Clear / Reset]
// ============================================================================
void Assembler::reset(bool releaseMemory) {
// CodeGen members.
_baseAddress = kNoBaseAddress;
_instOptions = 0;
_error = kErrorOk;
_baseZone.reset(releaseMemory);
// Assembler members.
if (releaseMemory && _buffer != NULL) {
ASMJIT_FREE(_buffer);
_buffer = NULL;
_end = NULL;
}
_cursor = _buffer;
_trampolineSize = 0;
_comment = NULL;
_unusedLinks = NULL;
_labelList.reset(releaseMemory);
_relocList.reset(releaseMemory);
}
// ============================================================================
// [asmjit::Assembler - Buffer]
// ============================================================================
Error Assembler::_grow(size_t n) {
size_t capacity = getCapacity();
size_t after = getOffset() + n;
// Overflow.
if (n > IntUtil::maxUInt<uintptr_t>() - capacity)
return setError(kErrorNoHeapMemory);
// Grow is called when allocation is needed, so it shouldn't happen, but on
// the other hand it is simple to catch and it's not an error.
if (after <= capacity)
return kErrorOk;
if (capacity < kMemAllocOverhead)
capacity = kMemAllocOverhead;
else
capacity += kMemAllocOverhead;
do {
size_t oldCapacity = capacity;
if (capacity < kMemAllocGrowMax)
capacity *= 2;
else
capacity += kMemAllocGrowMax;
// Overflow.
if (oldCapacity > capacity)
return setError(kErrorNoHeapMemory);
} while (capacity - kMemAllocOverhead < after);
capacity -= kMemAllocOverhead;
return _reserve(capacity);
}
Error Assembler::_reserve(size_t n) {
size_t capacity = getCapacity();
if (n <= capacity)
return kErrorOk;
uint8_t* newBuffer;
if (_buffer == NULL)
newBuffer = static_cast<uint8_t*>(ASMJIT_ALLOC(n));
else
newBuffer = static_cast<uint8_t*>(ASMJIT_REALLOC(_buffer, n));
if (newBuffer == NULL)
return setError(kErrorNoHeapMemory);
size_t offset = getOffset();
_buffer = newBuffer;
_end = _buffer + n;
_cursor = newBuffer + offset;
return kErrorOk;
}
// ============================================================================
// [asmjit::Assembler - Label]
// ============================================================================
Error Assembler::_registerIndexedLabels(size_t index) {
size_t i = _labelList.getLength();
if (index < i)
return kErrorOk;
if (_labelList._grow(index - i) != kErrorOk)
return setError(kErrorNoHeapMemory);
LabelData data;
data.offset = -1;
data.links = NULL;
do {
_labelList.append(data);
} while (++i < index);
return kErrorOk;
}
Error Assembler::_newLabel(Label* dst) {
dst->_label.op = kOperandTypeLabel;
dst->_label.size = 0;
dst->_label.id = OperandUtil::makeLabelId(static_cast<uint32_t>(_labelList.getLength()));
LabelData data;
data.offset = -1;
data.links = NULL;
if (_labelList.append(data) != kErrorOk)
goto _NoMemory;
return kErrorOk;
_NoMemory:
dst->_label.id = kInvalidValue;
return setError(kErrorNoHeapMemory);
}
LabelLink* Assembler::_newLabelLink() {
LabelLink* link = _unusedLinks;
if (link) {
_unusedLinks = link->prev;
}
else {
link = _baseZone.allocT<LabelLink>();
if (link == NULL)
return NULL;
}
link->prev = NULL;
link->offset = 0;
link->displacement = 0;
link->relocId = -1;
return link;
}
Error Assembler::bind(const Label& label) {
// Get label data based on label id.
uint32_t index = label.getId();
LabelData* data = getLabelData(index);
// Label can be bound only once.
if (data->offset != -1)
return setError(kErrorLabelAlreadyBound);
#if !defined(ASMJIT_DISABLE_LOGGER)
if (_logger)
_logger->logFormat(kLoggerStyleLabel, "L%u:\n", index);
#endif // !ASMJIT_DISABLE_LOGGER
Error error = kErrorOk;
size_t pos = getOffset();
LabelLink* link = data->links;
LabelLink* prev = NULL;
while (link) {
intptr_t offset = link->offset;
if (link->relocId != -1) {
// Handle RelocData - We have to update RelocData information instead of
// patching the displacement in LabelData.
_relocList[link->relocId].data += static_cast<Ptr>(pos);
}
else {
// Not using relocId, this means that we are overwriting a real
// displacement in the binary stream.
int32_t patchedValue = static_cast<int32_t>(
static_cast<intptr_t>(pos) - offset + link->displacement);
// Size of the value we are going to patch. Only BYTE/DWORD is allowed.
uint32_t size = getByteAt(offset);
ASMJIT_ASSERT(size == 1 || size == 4);
if (size == 4) {
setInt32At(offset, patchedValue);
}
else {
ASMJIT_ASSERT(size == 1);
if (IntUtil::isInt8(patchedValue))
setByteAt(offset, static_cast<uint8_t>(patchedValue & 0xFF));
else
error = kErrorIllegalDisplacement;
}
}
prev = link->prev;
link = prev;
}
// Chain unused links.
link = data->links;
if (link) {
if (prev == NULL)
prev = link;
prev->prev = _unusedLinks;
_unusedLinks = link;
}
// Set as bound (offset is zero or greater and no links).
data->offset = pos;
data->links = NULL;
if (error != kErrorOk)
return setError(error);
return error;
}
// ============================================================================
// [asmjit::Assembler - Embed]
// ============================================================================
Error Assembler::embed(const void* data, uint32_t size) {
if (getRemainingSpace() < size) {
Error error = _grow(size);
if (error != kErrorOk)
return setError(error);
}
uint8_t* cursor = getCursor();
::memcpy(cursor, data, size);
setCursor(cursor + size);
#if !defined(ASMJIT_DISABLE_LOGGER)
if (_logger)
_logger->logBinary(kLoggerStyleData, data, size);
#endif // !ASMJIT_DISABLE_LOGGER
return kErrorOk;
}
// ============================================================================
// [asmjit::Assembler - Reloc]
// ============================================================================
size_t Assembler::relocCode(void* dst, Ptr baseAddress) const {
if (baseAddress == kNoBaseAddress)
baseAddress = hasBaseAddress() ? getBaseAddress() : static_cast<Ptr>((uintptr_t)dst);
else if (getBaseAddress() != baseAddress)
return 0;
return _relocCode(dst, baseAddress);
}
// ============================================================================
// [asmjit::Assembler - Make]
// ============================================================================
void* Assembler::make() {
// Do nothing on error condition or if no instruction has been emitted.
if (_error != kErrorOk || getCodeSize() == 0)
return NULL;
void* p;
Error error = _runtime->add(&p, this);
if (error != kErrorOk)
setError(error);
return p;
}
// ============================================================================
// [asmjit::Assembler - Emit (Helpers)]
// ============================================================================
#define NA noOperand
Error Assembler::emit(uint32_t code) {
return _emit(code, NA, NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0) {
return _emit(code, o0, NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1) {
return _emit(code, o0, o1, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2) {
return _emit(code, o0, o1, o2, NA);
}
Error Assembler::emit(uint32_t code, int o0) {
return _emit(code, Imm(o0), NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, int o1) {
return _emit(code, o0, Imm(o1), NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, int o2) {
return _emit(code, o0, o1, Imm(o2), NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int o3) {
return _emit(code, o0, o1, o2, Imm(o3));
}
Error Assembler::emit(uint32_t code, int64_t o0) {
return _emit(code, Imm(o0), NA, NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, int64_t o1) {
return _emit(code, o0, Imm(o1), NA, NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, int64_t o2) {
return _emit(code, o0, o1, Imm(o2), NA);
}
Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int64_t o3) {
return _emit(code, o0, o1, o2, Imm(o3));
}
#undef NA
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_ASSEMBLER_H
#define _ASMJIT_BASE_ASSEMBLER_H
// [Dependencies - AsmJit]
#include "../base/codegen.h"
#include "../base/containers.h"
#include "../base/error.h"
#include "../base/logger.h"
#include "../base/operand.h"
#include "../base/runtime.h"
#include "../base/zone.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::InstId]
// ============================================================================
//! Instruction codes (stub).
ASMJIT_ENUM(InstId) {
//! No instruction.
kInstIdNone = 0
};
// ============================================================================
// [asmjit::InstOptions]
// ============================================================================
//! Instruction options.
ASMJIT_ENUM(InstOptions) {
//! No instruction options.
kInstOptionNone = 0x00000000,
//! Emit short form of the instruction.
//!
//! X86/X64:
//!
//! Short form is mostly related to jmp and jcc instructions, but can be used
//! by other instructions supporting 8-bit or 32-bit immediates. This option
//! can be dangerous if the short jmp/jcc is required, but not encodable due
//! to large displacement, in such case an error happens and the whole
//! assembler/compiler stream is unusable.
kInstOptionShortForm = 0x00000001,
//! Emit long form of the instruction.
//!
//! X86/X64:
//!
//! Long form is mosrlt related to jmp and jcc instructions, but like the
//! `kInstOptionShortForm` option it can be used by other instructions
//! supporting both 8-bit and 32-bit immediates.
kInstOptionLongForm = 0x00000002,
//! Condition is likely to be taken.
kInstOptionTaken = 0x00000004,
//! Condition is unlikely to be taken.
kInstOptionNotTaken = 0x00000008
};
// ============================================================================
// [asmjit::LabelLink]
// ============================================================================
//! \internal
//!
//! Data structure used to link linked-labels.
struct LabelLink {
//! Previous link.
LabelLink* prev;
//! Offset.
intptr_t offset;
//! Inlined displacement.
intptr_t displacement;
//! RelocId if link must be absolute when relocated.
intptr_t relocId;
};
// ============================================================================
// [asmjit::LabelData]
// ============================================================================
//! \internal
//!
//! Label data.
struct LabelData {
//! Label offset.
intptr_t offset;
//! Label links chain.
LabelLink* links;
};
// ============================================================================
// [asmjit::RelocData]
// ============================================================================
//! \internal
//!
//! Code relocation data (relative vs absolute addresses).
//!
//! X86/X64:
//!
//! X86 architecture uses 32-bit absolute addressing model by memory operands,
//! but 64-bit mode uses relative addressing model (RIP + displacement). In
//! code we are always using relative addressing model for referencing labels
//! and embedded data. In 32-bit mode we must patch all references to absolute
//! address before we can call generated function.
struct RelocData {
//! Type of relocation.
uint32_t type;
//! Size of relocation (4 or 8 bytes).
uint32_t size;
//! Offset from code begin address.
Ptr from;
//! Relative displacement from code begin address (not to `offset`) or
//! absolute address.
Ptr data;
};
// ============================================================================
// [asmjit::Assembler]
// ============================================================================
//! Base assembler.
//!
//! This class implements the base interface to an assembler. The architecture
//! specific API is implemented by backends.
//!
//! \sa Compiler.
struct ASMJIT_VCLASS Assembler : public CodeGen {
ASMJIT_NO_COPY(Assembler)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `Assembler` instance.
ASMJIT_API Assembler(Runtime* runtime);
//! Destroy the `Assembler` instance.
ASMJIT_API virtual ~Assembler();
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the assembler.
//!
//! If `releaseMemory` is true all buffers will be released to the system.
ASMJIT_API void reset(bool releaseMemory = false);
// --------------------------------------------------------------------------
// [Buffer]
// --------------------------------------------------------------------------
//! Get capacity of the code buffer.
ASMJIT_INLINE size_t getCapacity() const {
return (size_t)(_end - _buffer);
}
//! Get the number of remaining bytes (space between cursor and the end of
//! the buffer).
ASMJIT_INLINE size_t getRemainingSpace() const {
return (size_t)(_end - _cursor);
}
//! Get buffer.
ASMJIT_INLINE uint8_t* getBuffer() const {
return _buffer;
}
//! Get the end of the buffer (points to the first byte that is outside).
ASMJIT_INLINE uint8_t* getEnd() const {
return _end;
}
//! Get the current position in the buffer.
ASMJIT_INLINE uint8_t* getCursor() const {
return _cursor;
}
//! Set the current position in the buffer.
ASMJIT_INLINE void setCursor(uint8_t* cursor) {
ASMJIT_ASSERT(cursor >= _buffer && cursor <= _end);
_cursor = cursor;
}
//! Get the current offset in the buffer.
ASMJIT_INLINE size_t getOffset() const {
return (size_t)(_cursor - _buffer);
}
//! Set the current offset in the buffer to `offset` and get the previous
//! offset value.
ASMJIT_INLINE size_t setOffset(size_t offset) {
ASMJIT_ASSERT(offset < getCapacity());
size_t oldOffset = (size_t)(_cursor - _buffer);
_cursor = _buffer + offset;
return oldOffset;
}
//! Grow the internal buffer.
//!
//! The internal buffer will grow at least by `n` bytes so `n` bytes can be
//! added to it. If `n` is zero or `getOffset() + n` is not greater than the
//! current capacity of the buffer this function does nothing.
ASMJIT_API Error _grow(size_t n);
//! Reserve the internal buffer to at least `n` bytes.
ASMJIT_API Error _reserve(size_t n);
//! Get BYTE at position `pos`.
ASMJIT_INLINE uint8_t getByteAt(size_t pos) const {
ASMJIT_ASSERT(pos + 1 <= (size_t)(_end - _buffer));
return *reinterpret_cast<const uint8_t*>(_buffer + pos);
}
//! Get WORD at position `pos`.
ASMJIT_INLINE uint16_t getWordAt(size_t pos) const {
ASMJIT_ASSERT(pos + 2 <= (size_t)(_end - _buffer));
return *reinterpret_cast<const uint16_t*>(_buffer + pos);
}
//! Get DWORD at position `pos`.
ASMJIT_INLINE uint32_t getDWordAt(size_t pos) const {
ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer));
return *reinterpret_cast<const uint32_t*>(_buffer + pos);
}
//! Get QWORD at position `pos`.
ASMJIT_INLINE uint64_t getQWordAt(size_t pos) const {
ASMJIT_ASSERT(pos + 8 <= (size_t)(_end - _buffer));
return *reinterpret_cast<const uint64_t*>(_buffer + pos);
}
//! Get int32_t at position `pos`.
ASMJIT_INLINE int32_t getInt32At(size_t pos) const {
ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer));
return *reinterpret_cast<const int32_t*>(_buffer + pos);
}
//! Get uint32_t at position `pos`.
ASMJIT_INLINE uint32_t getUInt32At(size_t pos) const {
ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer));
return *reinterpret_cast<const uint32_t*>(_buffer + pos);
}
//! Set BYTE at position `pos`.
ASMJIT_INLINE void setByteAt(size_t pos, uint8_t x) {
ASMJIT_ASSERT(pos + 1 <= (size_t)(_end - _buffer));
*reinterpret_cast<uint8_t*>(_buffer + pos) = x;
}
//! Set WORD at position `pos`.
ASMJIT_INLINE void setWordAt(size_t pos, uint16_t x) {
ASMJIT_ASSERT(pos + 2 <= (size_t)(_end - _buffer));
*reinterpret_cast<uint16_t*>(_buffer + pos) = x;
}
//! Set DWORD at position `pos`.
ASMJIT_INLINE void setDWordAt(size_t pos, uint32_t x) {
ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer));
*reinterpret_cast<uint32_t*>(_buffer + pos) = x;
}
//! Set QWORD at position `pos`.
ASMJIT_INLINE void setQWordAt(size_t pos, uint64_t x) {
ASMJIT_ASSERT(pos + 8 <= (size_t)(_end - _buffer));
*reinterpret_cast<uint64_t*>(_buffer + pos) = x;
}
//! Set int32_t at position `pos`.
ASMJIT_INLINE void setInt32At(size_t pos, int32_t x) {
ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer));
*reinterpret_cast<int32_t*>(_buffer + pos) = x;
}
//! Set uint32_t at position `pos`.
ASMJIT_INLINE void setUInt32At(size_t pos, uint32_t x) {
ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer));
*reinterpret_cast<uint32_t*>(_buffer + pos) = x;
}
// --------------------------------------------------------------------------
// [GetCodeSize]
// --------------------------------------------------------------------------
//! Get current offset in buffer, same as `getOffset() + getTramplineSize()`.
ASMJIT_INLINE size_t getCodeSize() const {
return getOffset() + getTrampolineSize();
}
// --------------------------------------------------------------------------
// [GetTrampolineSize]
// --------------------------------------------------------------------------
//! Get size of all possible trampolines.
//!
//! Trampolines are needed to successfuly generate relative jumps to absolute
//! addresses. This value is only non-zero if jmp of call instructions were
//! used with immediate operand (this means jumping or calling an absolute
//! address directly).
ASMJIT_INLINE size_t getTrampolineSize() const {
return _trampolineSize;
}
// --------------------------------------------------------------------------
// [Label]
// --------------------------------------------------------------------------
//! Get number of labels created.
ASMJIT_INLINE size_t getLabelsCount() const {
return _labelList.getLength();
}
//! Get whether the `label` is valid (created by the assembler).
ASMJIT_INLINE bool isLabelValid(const Label& label) const {
return isLabelValid(label.getId());
}
//! \overload
ASMJIT_INLINE bool isLabelValid(uint32_t id) const {
return static_cast<size_t>(id) < _labelList.getLength();
}
//! Get whether the `label` is bound.
//!
//! \note It's an error to pass label that is not valid. Check the validity
//! of the label by using `isLabelValid()` method before the bound check if
//! you are not sure about its validity, otherwise you may hit an assertion
//! failure in debug mode, and undefined behavior in release mode.
ASMJIT_INLINE bool isLabelBound(const Label& label) const {
return isLabelBound(label.getId());
}
//! \overload
ASMJIT_INLINE bool isLabelBound(uint32_t id) const {
ASMJIT_ASSERT(isLabelValid(id));
return _labelList[id].offset != -1;
}
//! Get `label` offset or -1 if the label is not yet bound.
ASMJIT_INLINE intptr_t getLabelOffset(const Label& label) const {
return getLabelOffset(label.getId());
}
//! \overload
ASMJIT_INLINE intptr_t getLabelOffset(uint32_t id) const {
ASMJIT_ASSERT(isLabelValid(id));
return _labelList[id].offset;
}
//! Get `LabelData` by `label`.
ASMJIT_INLINE LabelData* getLabelData(const Label& label) const {
return getLabelData(label.getId());
}
//! \overload
ASMJIT_INLINE LabelData* getLabelData(uint32_t id) const {
ASMJIT_ASSERT(isLabelValid(id));
return const_cast<LabelData*>(&_labelList[id]);
}
//! \internal
//!
//! Register labels for other code generator, i.e. `Compiler`.
ASMJIT_API Error _registerIndexedLabels(size_t index);
//! \internal
//!
//! Create and initialize a new `Label`.
ASMJIT_API Error _newLabel(Label* dst);
//! \internal
//!
//! New LabelLink instance.
ASMJIT_API LabelLink* _newLabelLink();
//! Create and return a new `Label`.
ASMJIT_INLINE Label newLabel() {
Label result(NoInit);
_newLabel(&result);
return result;
}
//! Bind label to the current offset.
//!
//! \note Label can be bound only once!
ASMJIT_API virtual Error bind(const Label& label);
// --------------------------------------------------------------------------
// [Embed]
// --------------------------------------------------------------------------
//! Embed data into the code buffer.
ASMJIT_API virtual Error embed(const void* data, uint32_t size);
// --------------------------------------------------------------------------
// [Align]
// --------------------------------------------------------------------------
//! Align target buffer to `m` bytes.
//!
//! Typical usage of this is to align labels at start of the inner loops.
//!
//! Inserts `nop()` instructions or CPU optimized NOPs.
virtual Error align(uint32_t mode, uint32_t offset) = 0;
// --------------------------------------------------------------------------
// [Reloc]
// --------------------------------------------------------------------------
//! Relocate the code to `baseAddress` and copy to `dst`.
//!
//! \param dst Contains the location where the relocated code should be
//! copied. The pointer can be address returned by virtual memory allocator
//! or any other address that has sufficient space.
//!
//! \param base Base address used for relocation. The `JitRuntime` always
//! sets the `base` address to be the same as `dst`, but other runtimes, for
//! example `StaticRuntime`, do not have to follow this rule.
//!
//! \retval The number bytes actually used. If the code generator reserved
//! space for possible trampolines, but didn't use it, the number of bytes
//! used can actually be less than the expected worst case. Virtual memory
//! allocator can shrink the memory allocated first time.
//!
//! A given buffer will be overwritten, to get the number of bytes required,
//! use `getCodeSize()`.
ASMJIT_API size_t relocCode(void* dst, Ptr baseAddress = kNoBaseAddress) const;
//! \internal
//!
//! Reloc code.
virtual size_t _relocCode(void* dst, Ptr baseAddress) const = 0;
// --------------------------------------------------------------------------
// [Make]
// --------------------------------------------------------------------------
ASMJIT_API virtual void* make();
// --------------------------------------------------------------------------
// [Emit]
// --------------------------------------------------------------------------
//! Emit an instruction.
ASMJIT_API Error emit(uint32_t code);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2);
//! \overload
ASMJIT_INLINE Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3) {
return _emit(code, o0, o1, o2, o3);
}
//! Emit an instruction with integer immediate operand.
ASMJIT_API Error emit(uint32_t code, int o0);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, int o1);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, int o2);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int o3);
//! \overload
ASMJIT_API Error emit(uint32_t code, int64_t o0);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, int64_t o1);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, int64_t o2);
//! \overload
ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int64_t o3);
//! Emit an instruction (virtual).
virtual Error _emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3) = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Buffer where the code is emitted (either live or temporary).
//!
//! This is actually the base pointer of the buffer, to get the current
//! position (cursor) look at the `_cursor` member.
uint8_t* _buffer;
//! The end of the buffer (points to the first invalid byte).
//!
//! The end of the buffer is calculated as <code>_buffer + size</code>.
uint8_t* _end;
//! The current position in code `_buffer`.
uint8_t* _cursor;
//! Size of possible trampolines.
uint32_t _trampolineSize;
//! Inline comment that will be logged by the next instruction and set to NULL.
const char* _comment;
//! Unused `LabelLink` structures pool.
LabelLink* _unusedLinks;
//! LabelData list.
PodVector<LabelData> _labelList;
//! RelocData list.
PodVector<RelocData> _relocList;
};
//! \}
// ============================================================================
// [Defined-Later]
// ============================================================================
ASMJIT_INLINE Label::Label(Assembler& a) : Operand(NoInit) {
a._newLabel(this);
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_ASSEMBLER_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/codegen.h"
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CodeGen - Construction / Destruction]
// ============================================================================
CodeGen::CodeGen(Runtime* runtime) :
_runtime(runtime),
_logger(NULL),
_errorHandler(NULL),
_baseAddress(runtime->getBaseAddress()),
_arch(kArchNone),
_regSize(0),
_reserved(0),
_features(IntUtil::mask(kCodeGenOptimizedAlign)),
_instOptions(0),
_error(kErrorOk),
_baseZone(16384 - kZoneOverhead) {}
CodeGen::~CodeGen() {
if (_errorHandler != NULL)
_errorHandler->release();
}
// ============================================================================
// [asmjit::CodeGen - Logging]
// ============================================================================
#if !defined(ASMJIT_DISABLE_LOGGER)
Error CodeGen::setLogger(Logger* logger) {
_logger = logger;
return kErrorOk;
}
#endif // !ASMJIT_DISABLE_LOGGER
// ============================================================================
// [asmjit::CodeGen - Error]
// ============================================================================
Error CodeGen::setError(Error error, const char* message) {
if (error == kErrorOk) {
_error = kErrorOk;
return kErrorOk;
}
if (message == NULL) {
#if !defined(ASMJIT_DISABLE_NAMES)
message = ErrorUtil::asString(error);
#else
static const char noMessage[] = "";
message = noMessage;
#endif // ASMJIT_DISABLE_NAMES
}
// Error handler is called before logger so logging can be skipped if error
// has been handled.
ErrorHandler* handler = _errorHandler;
ASMJIT_TLOG("[ERROR] %s %s\n", message, !handler ? "(Possibly unhandled?)" : "");
if (handler != NULL && handler->handleError(error, message))
return error;
#if !defined(ASMJIT_DISABLE_LOGGER)
Logger* logger = _logger;
if (logger != NULL) {
logger->logFormat(kLoggerStyleComment,
"*** ERROR: %s (%u).\n", message, static_cast<unsigned int>(error));
}
#endif // !ASMJIT_DISABLE_LOGGER
// The handler->handleError() function may throw an exception or longjmp()
// to terminate the execution of setError(). This is the reason why we have
// delayed changing the _error member until now.
_error = error;
return error;
}
Error CodeGen::setErrorHandler(ErrorHandler* handler) {
ErrorHandler* oldHandler = _errorHandler;
if (oldHandler != NULL)
oldHandler->release();
if (handler != NULL)
handler = handler->addRef();
_errorHandler = handler;
return kErrorOk;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment