mirror of
https://github.com/love2d/love-android.git
synced 2026-08-13 00:50:56 +02:00
Compare commits
412 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07088eee88 | |||
| 3d1503f917 | |||
| 65c4223d0a | |||
| 1975903d2e | |||
| 860c109861 | |||
| e74a9ef791 | |||
| 55feb38fa1 | |||
| 6811eca7d2 | |||
| d1f74df686 | |||
| c3ed3b454b | |||
| 8a3d635cdb | |||
| a81931be2c | |||
| 7a0068535b | |||
| 034a6d6361 | |||
| 7a32a370f9 | |||
| 8687928a33 | |||
| 6d67b15e79 | |||
| ecb5076b3d | |||
| 3b80a5a766 | |||
| 69cfcd25fb | |||
| 8bbe71a6bb | |||
| d9ea1f60a5 | |||
| a3b1903250 | |||
| 044d5b5e34 | |||
| abd98a1378 | |||
| 86dbfffcfc | |||
| b2686d98e5 | |||
| 11d99352dc | |||
| f2970f47b1 | |||
| 8a882cabc0 | |||
| f7bc09eac0 | |||
| 8b77791aeb | |||
| eac9c7dc6a | |||
| d257a3b987 | |||
| af732a0f47 | |||
| 9b9cc15813 | |||
| eeb47f70c0 | |||
| 729c355674 | |||
| 81ff3acca7 | |||
| 0b106828b2 | |||
| 5187166c3d | |||
| c8ebfc3427 | |||
| bda684ae1c | |||
| 7ee3735d1a | |||
| 55853d5850 | |||
| f9eca86935 | |||
| 5d34bf94f3 | |||
| acb97d6f87 | |||
| a1868f713d | |||
| 569df256e3 | |||
| 349c1e3dda | |||
| 75fd79b37a | |||
| 187e408d0b | |||
| eddaf2d70a | |||
| a0740420e8 | |||
| 69dcd4b63b | |||
| a46d4211e6 | |||
| d72a5e4f1e | |||
| ca0fe2c4ca | |||
| feb20972b5 | |||
| 63d53f3584 | |||
| bed7d5c826 | |||
| 542560bb2d | |||
| 8e61af7669 | |||
| 612a0d0d0c | |||
| 2cd3d319d0 | |||
| b367b3733d | |||
| efaf0f7aa4 | |||
| 8164f11ecb | |||
| 7ec4135598 | |||
| 8ad534d227 | |||
| ba88c4107f | |||
| 645bae0831 | |||
| 9076e124fe | |||
| fd966a7298 | |||
| 17f92a71aa | |||
| 91ba32e893 | |||
| 2f5394098c | |||
| 17ec687714 | |||
| cd0747806d | |||
| b17ba72668 | |||
| 06e4082dfb | |||
| 4001bdab19 | |||
| cd2280386e | |||
| b6cb0d037e | |||
| c9831be4a4 | |||
| a3fc057064 | |||
| a3ea2d5ca0 | |||
| c802045cb2 | |||
| 25de1b5de3 | |||
| ab850bfc5d | |||
| 80078b3046 | |||
| ba0f568f15 | |||
| 444a61b945 | |||
| 52a80a4e54 | |||
| 383fe4aec6 | |||
| b3d9ceb314 | |||
| 61ed34b4c7 | |||
| 56ab3cef77 | |||
| ae0d89458a | |||
| ab03301730 | |||
| 89c9fc0a70 | |||
| f507d58891 | |||
| 552376fc7a | |||
| 1ff59f59e5 | |||
| df01c4568b | |||
| 5862a884bd | |||
| d023500320 | |||
| 0480f4bac7 | |||
| 59153900ad | |||
| 0008f9b49e | |||
| c315ddff8b | |||
| 7da70d9786 | |||
| ac3ce6af68 | |||
| 05150585bf | |||
| 5b51e05895 | |||
| e94fe1a70d | |||
| d59818bf69 | |||
| f56e03b47c | |||
| cf608eda1d | |||
| aaae1f4392 | |||
| 71c07360a5 | |||
| 596cffc9dc | |||
| 0fe970a575 | |||
| 7ff727cc2d | |||
| c97bc90527 | |||
| b96fd660f9 | |||
| 321fd15d99 | |||
| dfe836d39c | |||
| 96f7387ac5 | |||
| 1c0565b56e | |||
| 9c94f41470 | |||
| 5b9fbeacb5 | |||
| 0e41ef20b3 | |||
| 6c754106ea | |||
| f0abf9b537 | |||
| 31d7affd91 | |||
| 20329a65a6 | |||
| 8a964146c1 | |||
| b1740eb4b6 | |||
| cb56ef28fb | |||
| ba299eeafb | |||
| 3370e38eea | |||
| 7999e17a76 | |||
| 5a80990790 | |||
| 62684832a6 | |||
| 01c8018c36 | |||
| 1fe71865f6 | |||
| fa276ea4d8 | |||
| e721960f7e | |||
| 18c9b3c401 | |||
| face22cff4 | |||
| 8b93b1eea2 | |||
| 29821438ad | |||
| a199d54b50 | |||
| 61ef9802a3 | |||
| ee81c4262d | |||
| 4be53bca65 | |||
| c89ac83e37 | |||
| 29e49d9048 | |||
| 8c701dd3a4 | |||
| 59a5fcb875 | |||
| 05e193ddbd | |||
| 0380160775 | |||
| 376ae96cb1 | |||
| 8186794a03 | |||
| 54b29fca4f | |||
| 45507c5282 | |||
| f18cf70c15 | |||
| 5df17ba075 | |||
| 5090a072b2 | |||
| 117bba5ff2 | |||
| 78e88a7473 | |||
| 2529265eed | |||
| a2c79d95e0 | |||
| f761e43904 | |||
| cd6463b4e4 | |||
| 9298bed843 | |||
| 94d31aefc1 | |||
| 8d71234114 | |||
| 799956e002 | |||
| f8d0890519 | |||
| c065fe9e56 | |||
| 77c7570f55 | |||
| 6d5d8c4540 | |||
| 456273ebf0 | |||
| 7c48033960 | |||
| b8beb8a1f4 | |||
| c1399fc44a | |||
| 077c1a350b | |||
| 40ec52368b | |||
| 605ddefd0e | |||
| 2a23cbd16d | |||
| 0ebc4d9271 | |||
| 07149c0ba8 | |||
| 288f005d86 | |||
| 49ad4632b6 | |||
| 6eafa708f9 | |||
| 53d24a1b5f | |||
| 3b7407ab34 | |||
| e203f7ae4c | |||
| 1349f27bd8 | |||
| 9a11b88572 | |||
| f88e0ba4b9 | |||
| 2fd01541c2 | |||
| fc85623f10 | |||
| 692ce8bb51 | |||
| 0394a380c4 | |||
| 0000c326a8 | |||
| 34e88d43ae | |||
| 94c8a99ddc | |||
| 1ea86d3305 | |||
| 696fae730c | |||
| a19404a902 | |||
| e2c44417d1 | |||
| e23fb2c8ea | |||
| 4de76a9c52 | |||
| 973f9eef89 | |||
| 0ac43aa717 | |||
| c186a70b14 | |||
| 7fbf155d82 | |||
| 7ad34c64f4 | |||
| 3df1807c6f | |||
| 29a493a54b | |||
| c979f65152 | |||
| 75306e8e63 | |||
| 1dba069683 | |||
| ec8b710a93 | |||
| 408778fb8f | |||
| 45ae52f55e | |||
| f8241c1b65 | |||
| 91295b7506 | |||
| 4d8f4f70ce | |||
| ebd4a0ad4f | |||
| f40577c9cb | |||
| ac443d7e2f | |||
| fd60978b7b | |||
| 8c7772cfa4 | |||
| 6ed3ba3810 | |||
| 21ff4b2188 | |||
| ba269a5062 | |||
| 4ffea15ffc | |||
| 74d83c0c13 | |||
| 906a7cc00b | |||
| f12ab60594 | |||
| 5f4aeb2d71 | |||
| fb34e99d9a | |||
| d62a4df5ac | |||
| ad5db40a9d | |||
| 406d0c04e2 | |||
| a00675c4cc | |||
| 0afdf271a2 | |||
| 7ebcf9494e | |||
| cff1a295a6 | |||
| 234bc83aa2 | |||
| 84dc89e264 | |||
| a22628bc1b | |||
| 487f00201e | |||
| 1fe6d7b640 | |||
| 204945586e | |||
| 004ccf4f9c | |||
| f68cf2fcc4 | |||
| f6c360c809 | |||
| 391429367d | |||
| baa595bd51 | |||
| 957330d722 | |||
| ecaf3f7f45 | |||
| b3db9d4839 | |||
| a1aca69aa4 | |||
| 646de828b9 | |||
| 0ffa7f7db7 | |||
| 7d7e1cb1f9 | |||
| ee59b69e01 | |||
| 0ac0e39287 | |||
| 49d7b402a7 | |||
| 529b897fed | |||
| 7ef92ea876 | |||
| 40b2aa6e6a | |||
| 7c8b022dcb | |||
| dbdefdd86a | |||
| c931228238 | |||
| 15474cab41 | |||
| 2a0c579ebf | |||
| af039516c0 | |||
| cc457787f4 | |||
| 76230b01f9 | |||
| 3c922b0ae8 | |||
| ab888ac39f | |||
| fdb360a557 | |||
| d32e6ada77 | |||
| 46d624851b | |||
| 00cfe30282 | |||
| 0cf41c2792 | |||
| 803786eee9 | |||
| c025e5eab4 | |||
| 35cd9f1900 | |||
| 70f63954ed | |||
| f87d73671f | |||
| 967840db16 | |||
| 6dedec28a2 | |||
| 2358b45c27 | |||
| 08b116c09f | |||
| e9a3b2ac59 | |||
| 7345a01f56 | |||
| 42093cfbe9 | |||
| 8244228004 | |||
| 7459285690 | |||
| 7aaeb1485e | |||
| 5e410bca8e | |||
| 19e0b12363 | |||
| 24387a8450 | |||
| 962c894908 | |||
| 26a6c423f9 | |||
| 08d51fbf28 | |||
| eac5db7e10 | |||
| 28d2d92645 | |||
| 66685a2ade | |||
| 8f5ee4a05e | |||
| 686e1d5f3e | |||
| 7e877716d6 | |||
| ec5f068b19 | |||
| 8308a524bc | |||
| 4439a193d6 | |||
| 9cbddc2f5e | |||
| f3dd769e98 | |||
| c91dbb6957 | |||
| 198c2d28de | |||
| 357a7237a4 | |||
| 1924bbdeeb | |||
| d783f5afa1 | |||
| 7eb7db1a85 | |||
| 8af8dffad4 | |||
| a0efc89a7d | |||
| c30e788393 | |||
| 168357e1ad | |||
| d6f1d8bd70 | |||
| 3cfecd46d9 | |||
| f31760c445 | |||
| d0f866e4b0 | |||
| 542c029666 | |||
| 31cd05551e | |||
| 42f6beaa7e | |||
| 36216c16be | |||
| 013df025d0 | |||
| 535a8256ee | |||
| b732d6cc12 | |||
| 54215b7ce4 | |||
| fd9e9758a9 | |||
| f46115e794 | |||
| 29a3e4b566 | |||
| 02dc18a587 | |||
| a034220b10 | |||
| 72b2fcd4eb | |||
| b20f7e7302 | |||
| 6b1fae429b | |||
| 4ec2e26985 | |||
| 8df94d2a8a | |||
| 70eef137ae | |||
| 4f11bd0e7b | |||
| 5048f3ff8c | |||
| 66f600b5ab | |||
| 659b312121 | |||
| b875fffedb | |||
| 45b1fc2509 | |||
| c6ac06c366 | |||
| abb819335b | |||
| aba2f47b31 | |||
| ea459a43da | |||
| 6110de0fa8 | |||
| 8f20cb7cc5 | |||
| b25af54383 | |||
| c27f19c141 | |||
| d1ad6cc5a3 | |||
| afe33f06d2 | |||
| 70cb26bb2a | |||
| 8cb7bac09f | |||
| 49265f0596 | |||
| 1d29da2d80 | |||
| 6459889d32 | |||
| 6be242ef03 | |||
| 97d9154e48 | |||
| c40250e565 | |||
| 684adff826 | |||
| ec9be8a383 | |||
| d637b5c11d | |||
| adc4042d8b | |||
| e96eef4435 | |||
| 8acfb8d91a | |||
| cc829bdd1e | |||
| 7593d64761 | |||
| f09239788a | |||
| c0fd9a6e1b | |||
| 539ca8d4f2 | |||
| fab725ad8b | |||
| 5f6fa8c2b5 | |||
| 4c2846f4bb | |||
| cb2c852f58 | |||
| 60a58f9240 | |||
| 5849e8d369 | |||
| 1703660617 | |||
| 0db55463c9 | |||
| 288b25c585 | |||
| a5284dccf6 | |||
| 8069dc02c6 | |||
| 0bde453b2e | |||
| 667c4750ea | |||
| 38727b5c25 | |||
| 288a851a65 | |||
| 5e3579f680 | |||
| 1dd859c9d1 | |||
| 53d45533e0 | |||
| 3f13dc653f |
@@ -0,0 +1,61 @@
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
build_type: [Debug, Release]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup Java 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: adopt-hotspot
|
||||
java-version: 17
|
||||
cache: gradle
|
||||
- name: Build Normal Flavor
|
||||
run: bash ./gradlew assembleNormalRecord${{ matrix.build_type }}
|
||||
- name: Build Release-specific Binaries
|
||||
if: ${{ matrix.build_type == 'Release' }}
|
||||
run: bash ./gradlew assembleEmbedRelease bundleNormalNoRecordRelease bundleEmbedRecordRelease bundleEmbedNoRecordRelease
|
||||
- name: Artifact (Normal debug APK)
|
||||
if: ${{ matrix.build_type == 'Debug' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: love-android-debug.apk
|
||||
path: app/build/outputs/apk/normalRecord/debug/app-normal-record-debug.apk
|
||||
- name: Artifact (Normal unsigned APK)
|
||||
if: ${{ matrix.build_type == 'Release' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: love-android.apk
|
||||
path: app/build/outputs/apk/normalRecord/release/app-normal-record-release-unsigned.apk
|
||||
- name: Artifact (Normal AAB w/o recording)
|
||||
if: ${{ matrix.build_type == 'Release' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: love-android-ps.aab
|
||||
path: app/build/outputs/bundle/normalNoRecordRelease/app-normal-noRecord-release.aab
|
||||
- name: Artifact (Embed AAB)
|
||||
if: ${{ matrix.build_type == 'Release' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: love-android-embed-record.aab
|
||||
path: app/build/outputs/bundle/embedRecordRelease/app-embed-record-release.aab
|
||||
- name: Artifact (Embed AAB w/o recording)
|
||||
if: ${{ matrix.build_type == 'Release' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: love-android-embed.aab
|
||||
path: app/build/outputs/bundle/embedNoRecordRelease/app-embed-noRecord-release.aab
|
||||
- name: Artifact (Debug symbols)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: love-android-unstripped-debugsyms-${{ matrix.build_type }}
|
||||
path: love/build/intermediates/library_jni
|
||||
+17
-15
@@ -12,20 +12,22 @@ local.properties
|
||||
*.o
|
||||
*.so
|
||||
*.a
|
||||
*.orig
|
||||
*.rej
|
||||
|
||||
!jni/LuaJIT-2.0.1/android/armeabi/libluajit.a
|
||||
!jni/LuaJIT-2.0.1/android/armeabi-v7a/libluajit.a
|
||||
|
||||
jni/LuaJIT-2.0.1/buildandroid.sh
|
||||
jni/LuaJIT-2.0.1/src/host/buildvm
|
||||
jni/LuaJIT-2.0.1/src/host/buildvm_arch.h
|
||||
jni/LuaJIT-2.0.1/src/host/minilua
|
||||
jni/LuaJIT-2.0.1/src/jit/vmdef.lua
|
||||
jni/LuaJIT-2.0.1/src/libluajit.a
|
||||
jni/LuaJIT-2.0.1/src/lj_bcdef.h
|
||||
jni/LuaJIT-2.0.1/src/lj_ffdef.h
|
||||
jni/LuaJIT-2.0.1/src/lj_folddef.h
|
||||
jni/LuaJIT-2.0.1/src/lj_libdef.h
|
||||
jni/LuaJIT-2.0.1/src/lj_recdef.h
|
||||
jni/LuaJIT-2.0.1/src/lj_vm.s
|
||||
jni/LuaJIT-2.0.1/src/luajit
|
||||
# Downloaded gradle wrapper
|
||||
.gradle
|
||||
|
||||
# Android Studio project files
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
# Gradle build files
|
||||
build
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
|
||||
# love-android specific
|
||||
!app/src/main/assets/
|
||||
app/src/main/assets/game.love
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "love/src/jni/love"]
|
||||
path = love/src/jni/love
|
||||
url = https://github.com/love2d/love
|
||||
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>love_android_sdl2</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/[Löve] Generate Internal Scripts.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/ndk-build.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="org.love2d.android"
|
||||
android:versionCode="9"
|
||||
android:versionName="0.9.0-alpha9"
|
||||
android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<!-- Allow writing to external storage -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="LÖVE for Android"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
|
||||
<activity
|
||||
android:name="GameActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="LÖVE for Android"
|
||||
android:screenOrientation="landscape" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="tv.ouya.intent.category.GAME"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<!-- Android 2.3.3 -->
|
||||
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="18" />
|
||||
|
||||
<!-- OpenGL ES 2.0 -->
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
</manifest>
|
||||
@@ -1,37 +1,196 @@
|
||||
Android Port of LÖVE, the awesome 2D game engine LÖVE (http://love2d.org)
|
||||
Copyright (c) 2013-2014 Martin Felis <martin@fysx.org>
|
||||
Android Port of LÖVE, an awesome 2D game engine for Lua (http://love2d.org)
|
||||
Copyright (c) 2006-2024 LOVE Development Team
|
||||
|
||||

|
||||
|
||||
Download:
|
||||
---------
|
||||
|
||||
You can download pre-built Android packages from https://github.com/love2d/love/releases/latest that allow
|
||||
you to run .love files by opening them using a file manager of your choice.
|
||||
|
||||
Important:
|
||||
----------
|
||||
|
||||
If you want to build from source, make sure to clone the submodules too. Often errors include missing
|
||||
liblove.so and "Missing LÖVE" error when building. A proper way to clone this repository is:
|
||||
|
||||
```
|
||||
git clone --recurse-submodules https://github.com/love2d/love-android
|
||||
```
|
||||
|
||||
Add `-b <branch/tag name>` and `--depth 1` if needed.
|
||||
|
||||
If you already cloned the repository but forgot to initialize the submodules, execute:
|
||||
|
||||
```
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --force --recursive
|
||||
```
|
||||
|
||||
In the repository directory. For the last command, add `--depth 1` if needed.
|
||||
|
||||
Instructions:
|
||||
-------------
|
||||
|
||||
For detailed instructions please refer to the [wiki](wiki).
|
||||
Detailed instructions can be viewed at https://github.com/love2d/love-android/wiki
|
||||
|
||||
Quick Start:
|
||||
------------
|
||||
|
||||
Install the Android NDK and the Android SDK with SDK API 12 and run
|
||||
Before you start, install JDK 17 (not later not earlier). If you intend to build from Android Studio, skip this step as
|
||||
Android Studio bundles its own JDK 17.
|
||||
|
||||
ndk-build
|
||||
Install Android SDK with SDK API 34 (34.x.y) and Android NDK 25.2.9519653, set the environment variable
|
||||
`ANDROID_SDK_ROOT` to your Android SDK location and run:
|
||||
|
||||
and after that
|
||||
```
|
||||
$ ./gradlew assembleNormalRecord
|
||||
```
|
||||
|
||||
ant debug
|
||||
in the root folder of this project. This should give you an .apk file in the `app/build/outputs/apk/normalRecord`
|
||||
subdirectory that you can then sign and install on your phone. The `normalRecord` .apk variant is what you normally have
|
||||
when downloading APK from https://love2d.org.
|
||||
|
||||
in the root folder of this project or load the project using Eclipse and
|
||||
compile/run it using Eclipse.
|
||||
If you want to put your game inside the APK, you can either:
|
||||
|
||||
This should give you a .apk file in the bin/ subdirectory that you can then
|
||||
install on your phone.
|
||||
1. Put all your games in `app/src/embed/assets` such that your `main.lua` path is `app/src/embed/assets/main.lua`; or
|
||||
2. Put your zipped \*.love in `app/src/embed/assets` with name `game.love`
|
||||
|
||||
And change the application id, application version string and codes, application display name, and the icons (see
|
||||
[Game Packaging Wiki](https://github.com/love2d/love-android/wiki/Game-Packaging). Afterwards,
|
||||
run either `gradlew assembleEmbedNoRecordRelease` (or `gradlew assembleEmbedRecordRelease` if your game uses microphone) to
|
||||
generate APK which you can install or `gradlew bundleEmbedNoRecordRelease` (or `gradlew bundleEmbedRecordRelease`) to generate
|
||||
AAB which you can upload to Play Store.
|
||||
|
||||
Alternatively, you can install Android Studio **2022.3.1** or later. After opening it for the first time, open its SDK Manager
|
||||
and on the tab "SDK Tools", tick "Show Package Details" then select NDK (Side By Side) version 25.2.9519653. After that, open
|
||||
the repository root.
|
||||
|
||||
Notice: Previously, the embed + APKTool method is preferred, but recent announcements by Google render that method obsolete.
|
||||
|
||||
Bugs:
|
||||
-----
|
||||
|
||||
Bugs and feature requests should be reported to the [issue
|
||||
tracker](issues?status=new&status=open).
|
||||
Bugs and/or feature requests should be reported to the issue tracker at:
|
||||
* https://github.com/love2d/love-android/issues - LÖVE-Android-specific issues
|
||||
* https://github.com/love2d/love/issues - for LÖVE in general
|
||||
|
||||
Note: If in doubt, fill an issue report to https://github.com/love2d/love/issues. We'll transfer the issue to this repository as needed.
|
||||
|
||||
Changelog:
|
||||
----------
|
||||
|
||||
11.5:
|
||||
|
||||
* Contains all relevant changes for desktop LÖVE [11.5](https://love2d.org/wiki/11.5).
|
||||
* Fixed audio in Android still playing in the background in certain cases. ([love2d/love#1828](https://github.com/love2d/love/issues/1828))
|
||||
|
||||
|
||||
11.4:
|
||||
|
||||
* Contains all relevant changes for desktop LÖVE [11.4](https://love2d.org/wiki/11.4).
|
||||
* Changed how LOVE looks for game due to Android restrictions.
|
||||
* Fixed "pointer too large" and "bad lightuserdata" issue when compiling LOVE with recent Android SDK and running LOVE on Android 11 and later.
|
||||
* For Developers: Added experimental support for 3rd-party Lua modules. Please refer to `love/src/jni/lua-modules` for more information.
|
||||
* For Packaging: Added experimental fusing method. [See the FAQ for details](https://github.com/love2d/love-android/wiki/FAQ---Frequently-Asked-Questions#whats-experimental-fusing-method).
|
||||
|
||||
11.3:
|
||||
|
||||
* Contains all relevant changes for desktop LÖVE [11.3](https://love2d.org/wiki/11.3).
|
||||
* Added support for microphone recording on Android. **This is disabled in Play Store builds**.
|
||||
* Added t.audio.mic (`false` by default). On Android, setting it to true requests microphone recording permission from the user.
|
||||
* Fixed performance regression on Android devices with Adreno GPU.
|
||||
* Fixed video playback support on Android devices with Adreno GPU.
|
||||
|
||||
11.2:
|
||||
|
||||
* Contains all relevant changes for desktop LÖVE [11.2](https://love2d.org/wiki/11.2).
|
||||
* Added support for ARM64 devices to comply with Play Store requirements.
|
||||
* Fixed `love.system.openURL` crashing in some cases.
|
||||
* Changed target SDK to 28 so it comply with Play Store requirements.
|
||||
|
||||
0.10.2:
|
||||
|
||||
* Contains all relevant changes for desktop LÖVE [0.10.2](https://love2d.org/wiki/0.10.2).
|
||||
* Upgrade of SDL2 to 2.0.5 (fixes an issue with the accelerometer)
|
||||
|
||||
0.10.1:
|
||||
|
||||
* Contains all relevant changes for desktop LÖVE [0.10.1](https://love2d.org/wiki/0.10.1).
|
||||
* Added a new love.conf flag t.externalstorage, which determines whether files are saved in internal or external storage on Android devices.
|
||||
* Fixed audio on Android to pause when the app is inactive, and resume when the app becomes active again.
|
||||
* Fixed a driver bug on some Android devices which caused all objects to show up as black.
|
||||
* Fixed love.graphics.clear(colortable) causing crashes on OpenGL ES 2 systems when a Canvas is active.
|
||||
* New icons
|
||||
|
||||
0.10.0:
|
||||
|
||||
* first official release!
|
||||
* Disabled JIT by default as it can cause performance problems. To enable JIT call jit.on()
|
||||
|
||||
0.10.0-alpha2:
|
||||
|
||||
* Update to the next love API 0.10.0 (not yet officially released)
|
||||
* Added building of libtheora
|
||||
* Updated LuaJIT from 2.0.1 to 2.1
|
||||
* Fixed a compatibility issue with Android 2.3 devices
|
||||
* Updated libogg from 1.3.2. to 1.3.5
|
||||
* Updated OpenAL to 1.17.0
|
||||
* Updated SDL2 to a dev version of 2.0.4
|
||||
|
||||
0.9.2a:
|
||||
|
||||
* Added bugfix for ParticleSystem:clone
|
||||
|
||||
0.9.2:
|
||||
|
||||
* updated API to match that of LÖVE 0.9.2
|
||||
* love.window.setFullscreen can be used to switch between regular and immersive mode without status and navbar
|
||||
* added loading of games by opening a main.lua file
|
||||
* quitting LÖVE now conforms to the Android application lifecycle
|
||||
* stop vibrator when app is paused
|
||||
* fixed battery drain by properly pausing OpenAL device
|
||||
* fixed printing of non-number and non-string values
|
||||
* fixed compilation of Android NDK r10
|
||||
* fixed compilation warnings concerning ```APP_PLATFORM```
|
||||
* old instance is shut down when opening a new game (note: it may crash when opening games at a high frequency, e.g. more than 2 per second)
|
||||
* updated OpenAL-Soft to version 1.16.0
|
||||
* updated to newer SDL version (f9244b2a151)
|
||||
|
||||
0.9.1b:
|
||||
|
||||
* added love.system.vibrate(seconds)
|
||||
* print statements are now redirected to logcat. Output is prefixed with "[LOVE] "
|
||||
* removed DevIL, libpng, libjpeg, libmng, and libtiff
|
||||
* pngs are loaded using lodepng and jpegs using libturbo-jpeg
|
||||
* repeatedly fixed a bug which caused Release builds to crash
|
||||
* update to latest mobile-common branch
|
||||
|
||||
0.9.1a:
|
||||
|
||||
* using latest SDL\_androidgl.c (fixes some random performance issues)
|
||||
* using latest love-android @ changeset 8659be0e75a3 (adds support for
|
||||
compressed textures)
|
||||
|
||||
0.9.1:
|
||||
|
||||
* uses 0.9.1 API
|
||||
* fixed crash on Moto G (and possibly other devices). This was a nasty bug that would just show a blue screen without an error message. The bug was resolved using the help of headchant
|
||||
* fixed loading of jpegs (it probably hasn't worked up to now)
|
||||
* fixed issues with looping over active touches. This fix was sponsored by slime!
|
||||
|
||||
beta2:
|
||||
|
||||
* fixed bug with canvases
|
||||
* fixed writing of files when no identity in conf.lua was set
|
||||
* added file association (somewhat experimental)
|
||||
|
||||
beta1:
|
||||
|
||||
* fixed nasty crash on startup bug
|
||||
* fixed love.filesystem.getDirectoryItems()
|
||||
|
||||
alpha9:
|
||||
|
||||
* Packaged games do not get duplicated for loading, instead are loaded from memory (!!!)
|
||||
@@ -65,9 +224,5 @@ alpha7:
|
||||
License:
|
||||
--------
|
||||
|
||||
This project contains code from multiple projects using various licenses.
|
||||
Please look into the folders of jni/<projectname>/ for the respective
|
||||
licenses.
|
||||
|
||||
As for the other code and build system files, these are published under the
|
||||
zlib license (same as LÖVE).
|
||||
This project contains code from multiple projects using various licenses. Please look at LOVE
|
||||
[license.txt](https://github.com/love2d/love/blob/master/license.txt) for the respective licenses.
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
namespace "org.love2d.android.executable"
|
||||
ndkVersion '25.2.9519653'
|
||||
|
||||
defaultConfig {
|
||||
applicationId project.properties["app.application_id"]
|
||||
versionCode project.properties["app.version_code"].toInteger()
|
||||
versionName project.properties["app.version_name"]
|
||||
minSdk 18
|
||||
compileSdk 34
|
||||
targetSdk 34
|
||||
|
||||
def getAppName = {
|
||||
def nameArray = project.properties["app.name_byte_array"]
|
||||
def name = project.properties["app.name"]
|
||||
if (name != null && nameArray != null) {
|
||||
throw new Exception("Only define either `app.name` or `app.name_byte_array` in gradle.properties, but not both!")
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
def nameArraySplit = nameArray.split(",")
|
||||
def nameBytes = new byte[nameArraySplit.length]
|
||||
def count = 0
|
||||
for (String s: nameArraySplit) {
|
||||
nameBytes[count++] = (byte) Integer.parseInt(s)
|
||||
}
|
||||
return new String(nameBytes, StandardCharsets.UTF_8)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
manifestPlaceholders = [
|
||||
NAME:getAppName(),
|
||||
ORIENTATION:project.properties["app.orientation"],
|
||||
]
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
flavorDimensions = ['mode', 'recording']
|
||||
productFlavors {
|
||||
normal {
|
||||
dimension 'mode'
|
||||
}
|
||||
embed {
|
||||
dimension 'mode'
|
||||
}
|
||||
record {
|
||||
dimension 'recording'
|
||||
}
|
||||
noRecord {
|
||||
dimension 'recording'
|
||||
}
|
||||
}
|
||||
lint {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'androidx.multidex:multidex:2.0.1'
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
api 'androidx.appcompat:appcompat:1.6.1'
|
||||
api project(':love')
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/diglet/android-sdk-macosx/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
-keep class org.love2d.android.GameActivity { *; }
|
||||
-keep class org.libsdl.app.** { *; }
|
||||
@@ -0,0 +1,4 @@
|
||||
Add your game.love in this folder then
|
||||
$ ./gradlew bundleEmbed
|
||||
or
|
||||
$ ./gradlew assembleEmbed
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
|
||||
|
||||
<!-- OpenGL ES 2.0 -->
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
<!-- Touchscreen support -->
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
<!-- Game controller support -->
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.gamepad" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
|
||||
<!-- External mouse input events -->
|
||||
<uses-feature android:name="android.hardware.type.pc" android:required="false" />
|
||||
<!-- Low latency audio -->
|
||||
<uses-feature android:name="android.hardware.audio.low_latency" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.audio.pro" android:required="false" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/love"
|
||||
android:label="${NAME}"
|
||||
android:usesCleartextTraffic="true" >
|
||||
<activity
|
||||
android:name="org.love2d.android.GameActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation"
|
||||
android:label="${NAME}"
|
||||
android:launchMode="singleInstance"
|
||||
android:screenOrientation="${ORIENTATION}"
|
||||
android:resizeableActivity="false"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="tv.ouya.intent.category.GAME" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,5 @@
|
||||
values/generated_strings.xml
|
||||
values/generated_integers.xml
|
||||
drawable-*/icon.png
|
||||
drawable-*/notificationicon.png
|
||||
mipmap-*/ic_launcher.png
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
Executable → Regular
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,11 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" >
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" tools:replace="maxSdkVersion" />
|
||||
|
||||
<application tools:node="merge">
|
||||
<service android:name="org.love2d.android.DownloadService" />
|
||||
<activity
|
||||
android:name="org.love2d.android.GameActivity"
|
||||
tools:node="merge" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="file" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:pathPattern=".*\\.love" />
|
||||
<data android:host="*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="application/x-love-game" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.love2d.android.DownloadActivity"
|
||||
android:exported="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@android:style/Theme.NoTitleBar" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http"
|
||||
android:host="*"
|
||||
android:mimeType="*/*"
|
||||
android:pathPattern=".*\\.love" />
|
||||
<data android:scheme="https"
|
||||
android:host="*"
|
||||
android:mimeType="*/*"
|
||||
android:pathPattern=".*\\.love" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.love2d.android.SelectorActivity"
|
||||
android:label="LÖVE Loader"
|
||||
android:exported="true"
|
||||
android:enabled="@bool/selector_active"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="tv.ouya.intent.category.GAME" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,35 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint:deprecation"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.1.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This should be changed to the name of your project -->
|
||||
<project name="love_android_sdl2" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,19 @@
|
||||
# To set the application display name, only define either `app.name` or `app.name_byte_array`.
|
||||
# For a rule of thumb:
|
||||
# * Use `app.name` if your app name doesn't contain any non-ANSI characters.
|
||||
# * Otherwise, convert your app name to UTF-8 byte array, comma delimited, and put
|
||||
# it in `app.name_byte_array`
|
||||
#app.name=LÖVE for Android
|
||||
app.name_byte_array=76,195,150,86,69,32,102,111,114,32,65,110,100,114,111,105,100
|
||||
|
||||
app.application_id=org.love2d.android
|
||||
app.orientation=landscape
|
||||
app.version_code=32
|
||||
app.version_name=11.5a
|
||||
|
||||
# No need to modify anything past this line!
|
||||
android.enableJetifier=false
|
||||
android.useAndroidX=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=true
|
||||
android.nonFinalResIds=true
|
||||
Vendored
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,244 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+92
@@ -0,0 +1,92 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -1,9 +0,0 @@
|
||||
# Uncomment this if you're using STL in your project
|
||||
# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
|
||||
# APP_STL := stlport_static
|
||||
APP_STL := gnustl_shared
|
||||
APP_ABI := armeabi armeabi-v7a
|
||||
APP_CPPFLAGS := -frtti
|
||||
APP_LDFLAGS := -llog -landroid -lz
|
||||
|
||||
# APP_OPTIM := debug
|
||||
@@ -1,8 +0,0 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libluajit
|
||||
LOCAL_SRC_FILES := android/$(TARGET_ARCH_ABI)/libluajit.a
|
||||
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
@@ -1,56 +0,0 @@
|
||||
===============================================================================
|
||||
LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/
|
||||
|
||||
Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
|
||||
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 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.
|
||||
|
||||
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
|
||||
===============================================================================
|
||||
[ LuaJIT includes code from Lua 5.1/5.2, which has this license statement: ]
|
||||
|
||||
Copyright (C) 1994-2012 Lua.org, PUC-Rio.
|
||||
|
||||
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 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.
|
||||
|
||||
===============================================================================
|
||||
[ LuaJIT includes code from dlmalloc, which has this license statement: ]
|
||||
|
||||
This is a version (aka dlmalloc) of malloc/free/realloc written by
|
||||
Doug Lea and released to the public domain, as explained at
|
||||
http://creativecommons.org/licenses/publicdomain
|
||||
|
||||
===============================================================================
|
||||
@@ -1,149 +0,0 @@
|
||||
##############################################################################
|
||||
# LuaJIT top level Makefile for installation. Requires GNU Make.
|
||||
#
|
||||
# Please read doc/install.html before changing any variables!
|
||||
#
|
||||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
||||
# Note: src/Makefile has many more configurable options.
|
||||
#
|
||||
# ##### This Makefile is NOT useful for Windows! #####
|
||||
# For MSVC, please follow the instructions given in src/msvcbuild.bat.
|
||||
# For MinGW and Cygwin, cd to src and run make with the Makefile there.
|
||||
#
|
||||
# Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
##############################################################################
|
||||
|
||||
MAJVER= 2
|
||||
MINVER= 0
|
||||
RELVER= 1
|
||||
VERSION= $(MAJVER).$(MINVER).$(RELVER)
|
||||
ABIVER= 5.1
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Change the installation path as needed. This automatically adjusts
|
||||
# the paths in src/luaconf.h, too. Note: PREFIX must be an absolute path!
|
||||
#
|
||||
export PREFIX= /usr/local
|
||||
##############################################################################
|
||||
|
||||
DPREFIX= $(DESTDIR)$(PREFIX)
|
||||
INSTALL_BIN= $(DPREFIX)/bin
|
||||
INSTALL_LIB= $(DPREFIX)/lib
|
||||
INSTALL_SHARE= $(DPREFIX)/share
|
||||
INSTALL_INC= $(DPREFIX)/include/luajit-$(MAJVER).$(MINVER)
|
||||
|
||||
INSTALL_LJLIBD= $(INSTALL_SHARE)/luajit-$(VERSION)
|
||||
INSTALL_JITLIB= $(INSTALL_LJLIBD)/jit
|
||||
INSTALL_LMODD= $(INSTALL_SHARE)/lua
|
||||
INSTALL_LMOD= $(INSTALL_LMODD)/$(ABIVER)
|
||||
INSTALL_CMODD= $(INSTALL_LIB)/lua
|
||||
INSTALL_CMOD= $(INSTALL_CMODD)/$(ABIVER)
|
||||
INSTALL_MAN= $(INSTALL_SHARE)/man/man1
|
||||
INSTALL_PKGCONFIG= $(INSTALL_LIB)/pkgconfig
|
||||
|
||||
INSTALL_TNAME= luajit-$(VERSION)
|
||||
INSTALL_TSYMNAME= luajit
|
||||
INSTALL_ANAME= libluajit-$(ABIVER).a
|
||||
INSTALL_SONAME= libluajit-$(ABIVER).so.$(MAJVER).$(MINVER).$(RELVER)
|
||||
INSTALL_SOSHORT= libluajit-$(ABIVER).so
|
||||
INSTALL_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).$(MINVER).$(RELVER).dylib
|
||||
INSTALL_DYLIBSHORT1= libluajit-$(ABIVER).dylib
|
||||
INSTALL_DYLIBSHORT2= libluajit-$(ABIVER).$(MAJVER).dylib
|
||||
INSTALL_PCNAME= luajit.pc
|
||||
|
||||
INSTALL_STATIC= $(INSTALL_LIB)/$(INSTALL_ANAME)
|
||||
INSTALL_DYN= $(INSTALL_LIB)/$(INSTALL_SONAME)
|
||||
INSTALL_SHORT1= $(INSTALL_LIB)/$(INSTALL_SOSHORT)
|
||||
INSTALL_SHORT2= $(INSTALL_LIB)/$(INSTALL_SOSHORT)
|
||||
INSTALL_T= $(INSTALL_BIN)/$(INSTALL_TNAME)
|
||||
INSTALL_TSYM= $(INSTALL_BIN)/$(INSTALL_TSYMNAME)
|
||||
INSTALL_PC= $(INSTALL_PKGCONFIG)/$(INSTALL_PCNAME)
|
||||
|
||||
INSTALL_DIRS= $(INSTALL_BIN) $(INSTALL_LIB) $(INSTALL_INC) $(INSTALL_MAN) \
|
||||
$(INSTALL_PKGCONFIG) $(INSTALL_JITLIB) $(INSTALL_LMOD) $(INSTALL_CMOD)
|
||||
UNINSTALL_DIRS= $(INSTALL_JITLIB) $(INSTALL_LJLIBD) $(INSTALL_INC) \
|
||||
$(INSTALL_LMOD) $(INSTALL_LMODD) $(INSTALL_CMOD) $(INSTALL_CMODD)
|
||||
|
||||
RM= rm -f
|
||||
MKDIR= mkdir -p
|
||||
RMDIR= rmdir 2>/dev/null
|
||||
SYMLINK= ln -sf
|
||||
INSTALL_X= install -m 0755
|
||||
INSTALL_F= install -m 0644
|
||||
UNINSTALL= $(RM)
|
||||
LDCONFIG= ldconfig -n
|
||||
SED_PC= sed -e "s|^prefix=.*|prefix=$(PREFIX)|"
|
||||
|
||||
FILE_T= luajit
|
||||
FILE_A= libluajit.a
|
||||
FILE_SO= libluajit.so
|
||||
FILE_MAN= luajit.1
|
||||
FILE_PC= luajit.pc
|
||||
FILES_INC= lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h
|
||||
FILES_JITLIB= bc.lua v.lua dump.lua dis_x86.lua dis_x64.lua dis_arm.lua \
|
||||
dis_ppc.lua dis_mips.lua dis_mipsel.lua bcsave.lua vmdef.lua
|
||||
|
||||
ifeq (,$(findstring Windows,$(OS)))
|
||||
ifeq (Darwin,$(shell uname -s))
|
||||
INSTALL_SONAME= $(INSTALL_DYLIBNAME)
|
||||
INSTALL_SHORT1= $(INSTALL_LIB)/$(INSTALL_DYLIBSHORT1)
|
||||
INSTALL_SHORT2= $(INSTALL_LIB)/$(INSTALL_DYLIBSHORT2)
|
||||
LDCONFIG= :
|
||||
endif
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
|
||||
INSTALL_DEP= src/luajit
|
||||
|
||||
default all $(INSTALL_DEP):
|
||||
@echo "==== Building LuaJIT $(VERSION) ===="
|
||||
$(MAKE) -C src
|
||||
@echo "==== Successfully built LuaJIT $(VERSION) ===="
|
||||
|
||||
install: $(INSTALL_DEP)
|
||||
@echo "==== Installing LuaJIT $(VERSION) to $(PREFIX) ===="
|
||||
$(MKDIR) $(INSTALL_DIRS)
|
||||
cd src && $(INSTALL_X) $(FILE_T) $(INSTALL_T)
|
||||
cd src && test -f $(FILE_A) && $(INSTALL_F) $(FILE_A) $(INSTALL_STATIC) || :
|
||||
$(RM) $(INSTALL_TSYM) $(INSTALL_DYN) $(INSTALL_SHORT1) $(INSTALL_SHORT2)
|
||||
cd src && test -f $(FILE_SO) && \
|
||||
$(INSTALL_X) $(FILE_SO) $(INSTALL_DYN) && \
|
||||
$(LDCONFIG) $(INSTALL_LIB) && \
|
||||
$(SYMLINK) $(INSTALL_SONAME) $(INSTALL_SHORT1) && \
|
||||
$(SYMLINK) $(INSTALL_SONAME) $(INSTALL_SHORT2) || :
|
||||
cd etc && $(INSTALL_F) $(FILE_MAN) $(INSTALL_MAN)
|
||||
cd etc && $(SED_PC) $(FILE_PC) > $(FILE_PC).tmp && \
|
||||
$(INSTALL_F) $(FILE_PC).tmp $(INSTALL_PC) && \
|
||||
$(RM) $(FILE_PC).tmp
|
||||
cd src && $(INSTALL_F) $(FILES_INC) $(INSTALL_INC)
|
||||
cd src/jit && $(INSTALL_F) $(FILES_JITLIB) $(INSTALL_JITLIB)
|
||||
$(SYMLINK) $(INSTALL_TNAME) $(INSTALL_TSYM)
|
||||
@echo "==== Successfully installed LuaJIT $(VERSION) to $(PREFIX) ===="
|
||||
|
||||
uninstall:
|
||||
@echo "==== Uninstalling LuaJIT $(VERSION) from $(PREFIX) ===="
|
||||
$(UNINSTALL) $(INSTALL_TSYM) $(INSTALL_T) $(INSTALL_STATIC) $(INSTALL_DYN) $(INSTALL_SHORT1) $(INSTALL_SHORT2) $(INSTALL_MAN)/$(FILE_MAN) $(INSTALL_PC)
|
||||
for file in $(FILES_JITLIB); do \
|
||||
$(UNINSTALL) $(INSTALL_JITLIB)/$$file; \
|
||||
done
|
||||
for file in $(FILES_INC); do \
|
||||
$(UNINSTALL) $(INSTALL_INC)/$$file; \
|
||||
done
|
||||
$(LDCONFIG) $(INSTALL_LIB)
|
||||
$(RMDIR) $(UNINSTALL_DIRS) || :
|
||||
@echo "==== Successfully uninstalled LuaJIT $(VERSION) from $(PREFIX) ===="
|
||||
|
||||
##############################################################################
|
||||
|
||||
amalg:
|
||||
@echo "Building LuaJIT $(VERSION)"
|
||||
$(MAKE) -C src amalg
|
||||
|
||||
clean:
|
||||
$(MAKE) -C src clean
|
||||
|
||||
.PHONY: all install amalg clean
|
||||
|
||||
##############################################################################
|
||||
@@ -1,16 +0,0 @@
|
||||
README for LuaJIT 2.0.1
|
||||
-----------------------
|
||||
|
||||
LuaJIT is a Just-In-Time (JIT) compiler for the Lua programming language.
|
||||
|
||||
Project Homepage: http://luajit.org/
|
||||
|
||||
LuaJIT is Copyright (C) 2005-2013 Mike Pall.
|
||||
LuaJIT is free software, released under the MIT license.
|
||||
See full Copyright Notice in the COPYRIGHT file or in luajit.h.
|
||||
|
||||
Documentation for LuaJIT is available in HTML format.
|
||||
Please point your favorite browser to:
|
||||
|
||||
doc/luajit.html
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,58 +0,0 @@
|
||||
#!/bin/bash
|
||||
#================================================================#
|
||||
# Build script based on
|
||||
# https://github.com/moai/moai-dev/blob/master/ant/libmoai/jni/luajit/build.sh
|
||||
#================================================================#
|
||||
|
||||
host_os=`uname -s | tr "[:upper:]" "[:lower:]"`
|
||||
host_arch=`uname -m`
|
||||
|
||||
if [ -f android/armeabi/libluajit.a ] &&
|
||||
[ -f android/armeabi-v7a/libluajit.a ]; then
|
||||
echo "LuaJIT Already built"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
NDK_BUILD_LOCATION=${ANDROID_NDK}
|
||||
if [[ x$NDK_BUILD_LOCATION = "x" ]]; then
|
||||
echo "The Android NDK must be on your path."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NDK="${NDK_BUILD_LOCATION%/ndk-build}"
|
||||
|
||||
if [[ x$NDK = "x" ]]; then
|
||||
echo "The Android NDK must be on your path."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NDKABI=8
|
||||
NDKVER=$NDK/toolchains/arm-linux-androideabi-4.6
|
||||
NDKP=$NDKVER/prebuilt/${host_os}-${host_arch}/bin/arm-linux-androideabi-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-arm"
|
||||
CFLAGS=""
|
||||
|
||||
buildLuaJIT()
|
||||
{
|
||||
arch="$1"
|
||||
ndkarch="$2"
|
||||
DESTDIR=../android/$arch
|
||||
mkdir -p $DESTDIR 2>/dev/null
|
||||
rm "$DESTDIR"/*.a 2>/dev/null
|
||||
make clean
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_SYS=Linux TARGET_FLAGS="$NDKF $ndkarch" TARGET_CFLAGS="$CFLAGS" libluajit.a
|
||||
|
||||
if [ -f libluajit.a ]; then
|
||||
mv libluajit.a $DESTDIR/libluajit.a
|
||||
fi;
|
||||
}
|
||||
|
||||
cd src
|
||||
|
||||
# Android/ARM, armeabi (ARMv5TE soft-float), Android 2.2+ (Froyo)
|
||||
buildLuaJIT armeabi
|
||||
|
||||
# Android/ARM, armeabi-v7a (ARMv7 VFP), Android 4.0+ (ICS)
|
||||
buildLuaJIT armeabi-v7a "-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -Wl,--fix-cortex-a8"
|
||||
|
||||
make clean
|
||||
@@ -1,862 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>LuaJIT Change History</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Mike Pall">
|
||||
<meta name="Copyright" content="Copyright (C) 2005-2013, Mike Pall">
|
||||
<meta name="Language" content="en">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
||||
<style type="text/css">
|
||||
div.major { max-width: 600px; padding: 1em; margin: 1em 0 1em 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>LuaJIT Change History</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="status.html">Status</a>
|
||||
<ul><li>
|
||||
<a class="current" href="changes.html">Changes</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="faq.html">FAQ</a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
This is a list of changes between the released versions of LuaJIT.<br>
|
||||
The current <span style="color: #0000c0;">stable version</span> is <strong>LuaJIT 2.0.1</strong>.<br>
|
||||
</p>
|
||||
<p>
|
||||
Please check the
|
||||
<a href="http://luajit.org/changes.html"><span class="ext">»</span> Online Change History</a>
|
||||
to see whether newer versions are available.
|
||||
</p>
|
||||
|
||||
<div class="major" style="background: #d0d0ff;">
|
||||
<h2 id="LuaJIT-2.0.1">LuaJIT 2.0.1 — 2013-02-19</h2>
|
||||
<ul>
|
||||
<li>Don't clear frame for out-of-memory error.</li>
|
||||
<li>Leave hook when resume catches error thrown from hook.</li>
|
||||
<li>Add missing GC steps for template table creation.</li>
|
||||
<li>Fix discharge order of comparisons in Lua parser.</li>
|
||||
<li>Improve buffer handling for <tt>io.read()</tt>.</li>
|
||||
<li>OSX: Add support for Mach-O object files to <tt>-b</tt> option.</li>
|
||||
<li>Fix PS3 port.</li>
|
||||
<li>Fix/enable Xbox 360 port.</li>
|
||||
<li>x86/x64: Always mark ref for shift count as non-weak.</li>
|
||||
<li>x64: Don't fuse implicitly 32-to-64 extended operands.</li>
|
||||
<li>ARM: Fix armhf call argument handling.</li>
|
||||
<li>ARM: Fix code generation for integer math.min/math.max.</li>
|
||||
<li>PPC/e500: Fix <tt>lj_vm_floor()</tt> for Inf/NaN.</li>
|
||||
<li>FFI: Change priority of table initializer variants for structs.</li>
|
||||
<li>FFI: Fix code generation for bool call result check on x86/x64.</li>
|
||||
<li>FFI: Load FFI library on-demand for bytecode with cdata literals.</li>
|
||||
<li>FFI: Fix handling of qualified transparent structs/unions.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0">LuaJIT 2.0.0 — 2012-11-08</h2>
|
||||
<ul>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix Android/x86 build.</li>
|
||||
<li>Fix recording of equality comparisons with <tt>__eq</tt> metamethods.</li>
|
||||
<li>Fix detection of immutable upvalues.</li>
|
||||
<li>Replace error with PANIC for callbacks from JIT-compiled code.</li>
|
||||
<li>Fix builtin string to number conversion for <tt>INT_MIN</tt>.</li>
|
||||
<li>Don't create unneeded array part for template tables.</li>
|
||||
<li>Fix <tt>CONV.num.int</tt> sinking.</li>
|
||||
<li>Don't propagate implicitly widened number to index metamethods.</li>
|
||||
<li>ARM: Fix ordered comparisons of number vs. non-number.</li>
|
||||
<li>FFI: Fix code generation for replay of sunk float fields.</li>
|
||||
<li>FFI: Fix signedness of bool.</li>
|
||||
<li>FFI: Fix recording of bool call result check on x86/x64.</li>
|
||||
<li>FFI: Fix stack-adjustment for <tt>__thiscall</tt> callbacks.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta11">LuaJIT 2.0.0-beta11 — 2012-10-16</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>Use ARM VFP instructions, if available (build-time detection).</li>
|
||||
<li>Add support for ARM hard-float EABI (<tt>armhf</tt>).</li>
|
||||
<li>Add PS3 port.</li>
|
||||
<li>Add many features from Lua 5.2, e.g. <tt>goto</tt>/labels.
|
||||
Refer to <a href="extensions.html#lua52">this list</a>.</li>
|
||||
<li>FFI: Add parameterized C types.</li>
|
||||
<li>FFI: Add support for copy constructors.</li>
|
||||
<li>FFI: Equality comparisons never raise an error (treat as unequal instead).</li>
|
||||
<li>FFI: Box all accessed or returned enums.</li>
|
||||
<li>FFI: Check for <tt>__new</tt> metamethod when calling a constructor.</li>
|
||||
<li>FFI: Handle <tt>__pairs</tt>/<tt>__ipairs</tt> metamethods for cdata objects.</li>
|
||||
<li>FFI: Convert <tt>io.*</tt> file handle to <tt>FILE *</tt> pointer (but as a <tt>void *</tt>).</li>
|
||||
<li>FFI: Detect and support type punning through unions.</li>
|
||||
<li>FFI: Improve various error messages.</li>
|
||||
</ul></li>
|
||||
<li>Build-system reorganization:
|
||||
<ul>
|
||||
<li>Reorganize directory layout:<br>
|
||||
<tt>lib/*</tt> → <tt>src/jit/*</tt><br>
|
||||
<tt>src/buildvm_*.dasc</tt> → <tt>src/vm_*.dasc</tt><br>
|
||||
<tt>src/buildvm_*.h</tt> → removed<br>
|
||||
<tt>src/buildvm*</tt> → <tt>src/host/*</tt></li>
|
||||
<li>Add minified Lua interpreter plus Lua BitOp (<tt>minilua</tt>) to run DynASM.</li>
|
||||
<li>Change DynASM bit operations to use Lua BitOp</li>
|
||||
<li>Translate only <tt>vm_*.dasc</tt> for detected target architecture.</li>
|
||||
<li>Improve target detection for <tt>msvcbuild.bat</tt>.</li>
|
||||
<li>Fix build issues on Cygwin and MinGW with optional MSys.</li>
|
||||
<li>Handle cross-compiles with FPU/no-FPU or hard-fp/soft-fp ABI mismatch.</li>
|
||||
<li>Remove some library functions for no-JIT/no-FFI builds.</li>
|
||||
<li>Add uninstall target to top-level Makefile.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Preserve snapshot #0 PC for all traces.</li>
|
||||
<li>Fix argument checks for <tt>coroutine.create()</tt>.</li>
|
||||
<li>Command line prints version and JIT status to <tt>stdout</tt>, not <tt>stderr</tt>.</li>
|
||||
<li>Fix userdata <tt>__gc</tt> separations at Lua state close.</li>
|
||||
<li>Fix <tt>TDUP</tt> to <tt>HLOAD</tt> forwarding for <tt>LJ_DUALNUM</tt> builds.</li>
|
||||
<li>Fix buffer check in bytecode writer.</li>
|
||||
<li>Make <tt>os.date()</tt> thread-safe.</li>
|
||||
<li>Add missing declarations for MSVC intrinsics.</li>
|
||||
<li>Fix dispatch table modifications for return hooks.</li>
|
||||
<li>Workaround for MSVC conversion bug (<tt>double</tt> → <tt>uint32_t</tt> → <tt>int32_t</tt>).</li>
|
||||
<li>Fix FOLD rule <tt>(i-j)-i => 0-j</tt>.</li>
|
||||
<li>Never use DWARF unwinder on Windows.</li>
|
||||
<li>Fix shrinking of direct mapped blocks in builtin allocator.</li>
|
||||
<li>Limit recursion depth in <tt>string.match()</tt> et al.</li>
|
||||
<li>Fix late despecialization of <tt>ITERN</tt> after loop has been entered.</li>
|
||||
<li>Fix <tt>'f'</tt> and <tt>'L'</tt> options for <tt>debug.getinfo()</tt> and <tt>lua_getinfo()</tt>.</li>
|
||||
<li>Fix <tt>package.searchpath()</tt>.</li>
|
||||
<li>OSX: Change dylib names to be consistent with other platforms.</li>
|
||||
<li>Android: Workaround for broken <tt>sprintf("%g", -0.0)</tt>.</li>
|
||||
<li>x86: Remove support for ancient CPUs without <tt>CMOV</tt> (before Pentium Pro).</li>
|
||||
<li>x86: Fix register allocation for calls returning register pair.</li>
|
||||
<li>x86/x64: Fix fusion of unsigned byte comparisons with swapped operands.</li>
|
||||
<li>ARM: Fix <tt>tonumber()</tt> argument check.</li>
|
||||
<li>ARM: Fix modulo operator and <tt>math.floor()</tt>/<tt>math.ceil()</tt> for <tt>inf</tt>/<tt>nan</tt>.</li>
|
||||
<li>ARM: Invoke SPLIT pass for leftover <tt>IR_TOBIT</tt>.</li>
|
||||
<li>ARM: Fix BASE register coalescing.</li>
|
||||
<li>PPC: Fix interpreter state setup in callbacks.</li>
|
||||
<li>PPC: Fix <tt>string.sub()</tt> range check.</li>
|
||||
<li>MIPS: Support generation of MIPS/MIPSEL bytecode object files.</li>
|
||||
<li>MIPS: Fix calls to <tt>floor()</tt>/<tt>ceil()</tt><tt>/trunc()</tt>.</li>
|
||||
<li>ARM/PPC: Detect more target architecture variants.</li>
|
||||
<li>ARM/PPC/e500/MIPS: Fix tailcalls from fast functions, esp. <tt>tostring()</tt>.</li>
|
||||
<li>ARM/PPC/MIPS: Fix rematerialization of FP constants.</li>
|
||||
<li>FFI: Don't call <tt>FreeLibrary()</tt> on our own EXE/DLL.</li>
|
||||
<li>FFI: Resolve metamethods for constructors, too.</li>
|
||||
<li>FFI: Properly disable callbacks on iOS (would require executable memory).</li>
|
||||
<li>FFI: Fix cdecl string parsing during recording.</li>
|
||||
<li>FFI: Show address pointed to for <tt>tostring(ref)</tt>, too.</li>
|
||||
<li>FFI: Fix alignment of C call argument/return structure.</li>
|
||||
<li>FFI: Initialize all fields of standard types.</li>
|
||||
<li>FFI: Fix callback handling when new C types are declared in callback.</li>
|
||||
<li>FFI: Fix recording of constructors for pointers.</li>
|
||||
<li>FFI: Always resolve metamethods for pointers to structs.</li>
|
||||
<li>FFI: Correctly propagate alignment when interning nested types.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Add allocation sinking and store sinking optimization.</li>
|
||||
<li>Constify immutable upvalues.</li>
|
||||
<li>Add builtin string to integer or FP number conversion. Improves cross-platform consistency and correctness.</li>
|
||||
<li>Create string hash slots in template tables for non-const values, too. Avoids later table resizes.</li>
|
||||
<li>Eliminate <tt>HREFK</tt> guard for template table references.</li>
|
||||
<li>Add various new FOLD rules.</li>
|
||||
<li>Don't use stack unwinding for <tt>lua_yield()</tt> (slow on x64).</li>
|
||||
<li>ARM, PPC, MIPS: Improve <tt>XLOAD</tt> operand fusion and register hinting.</li>
|
||||
<li>PPC, MIPS: Compile <tt>math.sqrt()</tt> to sqrt instruction, if available.</li>
|
||||
<li>FFI: Fold <tt>KPTR</tt> + constant offset in SPLIT pass.</li>
|
||||
<li>FFI: Optimize/inline <tt>ffi.copy()</tt> and <tt>ffi.fill()</tt>.</li>
|
||||
<li>FFI: Compile and optimize array/struct copies.</li>
|
||||
<li>FFI: Compile <tt>ffi.typeof(cdata|ctype)</tt>, <tt>ffi.sizeof()</tt>, <tt>ffi.alignof()</tt>, <tt>ffi.offsetof()</tt> and <tt>ffi.gc()</tt>.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta10">LuaJIT 2.0.0-beta10 — 2012-05-09</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>The MIPS of LuaJIT is complete. It requires a CPU conforming to the
|
||||
MIPS32 R1 architecture with hardware FPU. O32 hard-fp ABI,
|
||||
little-endian or big-endian.</li>
|
||||
<li>Auto-detect target arch via cross-compiler. No need for
|
||||
<tt>TARGET=arch</tt> anymore.</li>
|
||||
<li>Make DynASM compatible with Lua 5.2.</li>
|
||||
<li>From Lua 5.2: Try <tt>__tostring</tt> metamethod on non-string error
|
||||
messages..</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix parsing of hex literals with exponents.</li>
|
||||
<li>Fix bytecode dump for certain number constants.</li>
|
||||
<li>Fix argument type in error message for relative arguments.</li>
|
||||
<li>Fix argument error handling on Lua stacks without a frame.</li>
|
||||
<li>Add missing mcode limit check in assembler backend.</li>
|
||||
<li>Fix compilation on OpenBSD.</li>
|
||||
<li>Avoid recursive GC steps after GC-triggered trace exit.</li>
|
||||
<li>Replace <tt><unwind.h></tt> definitions with our own.</li>
|
||||
<li>Fix OSX build issues. Bump minimum required OSX version to 10.4.</li>
|
||||
<li>Fix discharge order of comparisons in Lua parser.</li>
|
||||
<li>Ensure running <tt>__gc</tt> of userdata created in <tt>__gc</tt>
|
||||
at state close.</li>
|
||||
<li>Limit number of userdata <tt>__gc</tt> separations at state close.</li>
|
||||
<li>Fix bytecode <tt>JMP</tt> slot range when optimizing
|
||||
<tt>and</tt>/<tt>or</tt> with constant LHS.</li>
|
||||
<li>Fix DSE of <tt>USTORE</tt>.</li>
|
||||
<li>Make <tt>lua_concat()</tt> work from C hook with partial frame.</li>
|
||||
<li>Add required PHIs for implicit conversions, e.g. via <tt>XREF</tt>
|
||||
forwarding.</li>
|
||||
<li>Add more comparison variants to Valgrind suppressions file.</li>
|
||||
<li>Disable loading bytecode with an extra header (BOM or <tt>#!</tt>).</li>
|
||||
<li>Fix PHI stack slot syncing.</li>
|
||||
<li>ARM: Reorder type/value tests to silence Valgrind.</li>
|
||||
<li>ARM: Fix register allocation for <tt>ldrd</tt>-optimized
|
||||
<tt>HREFK</tt>.</li>
|
||||
<li>ARM: Fix conditional branch fixup for <tt>OBAR</tt>.</li>
|
||||
<li>ARM: Invoke SPLIT pass for <tt>double</tt> args in FFI call.</li>
|
||||
<li>ARM: Handle all <tt>CALL*</tt> ops with <tt>double</tt> results in
|
||||
SPLIT pass.</li>
|
||||
<li>ARM: Fix rejoin of <tt>POW</tt> in SPLIT pass.</li>
|
||||
<li>ARM: Fix compilation of <tt>math.sinh</tt>, <tt>math.cosh</tt>,
|
||||
<tt>math.tanh</tt>.</li>
|
||||
<li>ARM, PPC: Avoid pointless arg clearing in <tt>BC_IFUNCF</tt>.</li>
|
||||
<li>PPC: Fix resume after yield from hook.</li>
|
||||
<li>PPC: Fix argument checking for <tt>rawget()</tt>.</li>
|
||||
<li>PPC: Fix fusion of floating-point <tt>XLOAD</tt>/<tt>XSTORE</tt>.</li>
|
||||
<li>PPC: Fix <tt>HREFK</tt> code generation for huge tables.</li>
|
||||
<li>PPC: Use builtin D-Cache/I-Cache sync code.</li>
|
||||
</ul></li>
|
||||
<li>FFI library:
|
||||
<ul>
|
||||
<li>Ignore empty statements in <tt>ffi.cdef()</tt>.</li>
|
||||
<li>Ignore number parsing errors while skipping definitions.</li>
|
||||
<li>Don't touch frame in callbacks with tailcalls to fast functions.</li>
|
||||
<li>Fix library unloading on POSIX systems.</li>
|
||||
<li>Finalize cdata before userdata when closing the state.</li>
|
||||
<li>Change <tt>ffi.load()</tt> library name resolution for Cygwin.</li>
|
||||
<li>Fix resolving of function name redirects on Windows/x86.</li>
|
||||
<li>Fix symbol resolving error messages on Windows.</li>
|
||||
<li>Fix blacklisting of C functions calling callbacks.</li>
|
||||
<li>Fix result type of pointer difference.</li>
|
||||
<li>Use correct PC in FFI metamethod error message.</li>
|
||||
<li>Allow <tt>'typedef _Bool int BOOL;'</tt> for the Windows API.</li>
|
||||
<li>Don't record test for bool result of call, if ignored.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta9">LuaJIT 2.0.0-beta9 — 2011-12-14</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>PPC port of LuaJIT is complete. Default is the dual-number port
|
||||
(usually faster). Single-number port selectable via <tt>src/Makefile</tt>
|
||||
at build time.</li>
|
||||
<li>Add FFI callback support.</li>
|
||||
<li>Extend <tt>-b</tt> to generate <tt>.c</tt>, <tt>.h</tt> or <tt>.obj/.o</tt>
|
||||
files with embedded bytecode.</li>
|
||||
<li>Allow loading embedded bytecode with <tt>require()</tt>.</li>
|
||||
<li>From Lua 5.2: Change to <tt>'\z'</tt> escape. Reject undefined escape
|
||||
sequences.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix OSX 10.7 build. Fix <tt>install_name</tt> and versioning on OSX.</li>
|
||||
<li>Fix iOS build.</li>
|
||||
<li>Install <tt>dis_arm.lua</tt>, too.</li>
|
||||
<li>Mark installed shared library as executable.</li>
|
||||
<li>Add debug option to <tt>msvcbuild.bat</tt> and improve error handling.</li>
|
||||
<li>Fix data-flow analysis for iterators.</li>
|
||||
<li>Fix forced unwinding triggered by external unwinder.</li>
|
||||
<li>Record missing <tt>for</tt> loop slot loads (return to lower frame).</li>
|
||||
<li>Always use ANSI variants of Windows system functions.</li>
|
||||
<li>Fix GC barrier for multi-result table constructor (<tt>TSETM</tt>).</li>
|
||||
<li>Fix/add various FOLD rules.</li>
|
||||
<li>Add potential PHI for number conversions due to type instability.</li>
|
||||
<li>Do not eliminate PHIs only referenced from other PHIs.</li>
|
||||
<li>Correctly anchor implicit number to string conversions in Lua/C API.</li>
|
||||
<li>Fix various stack limit checks.</li>
|
||||
<li>x64: Use thread-safe exceptions for external unwinding (GCC platforms).</li>
|
||||
<li>x64: Fix result type of cdata index conversions.</li>
|
||||
<li>x64: Fix <tt>math.random()</tt> and <tt>bit.bswap()</tt> code generation.</li>
|
||||
<li>x64: Fix <tt>lightuserdata</tt> comparisons.</li>
|
||||
<li>x64: Always extend stack-passed arguments to pointer size.</li>
|
||||
<li>ARM: Many fixes to code generation backend.</li>
|
||||
<li>PPC/e500: Fix dispatch for binop metamethods.</li>
|
||||
<li>PPC/e500: Save/restore condition registers when entering/leaving the VM.</li>
|
||||
<li>PPC/e500: Fix write barrier in stores of strings to upvalues.</li>
|
||||
</ul></li>
|
||||
<li>FFI library:
|
||||
<ul>
|
||||
<li>Fix C comment parsing.</li>
|
||||
<li>Fix snapshot optimization for cdata comparisons.</li>
|
||||
<li>Fix recording of const/enum lookups in namespaces.</li>
|
||||
<li>Fix call argument and return handling for <tt>I8/U8/I16/U16</tt> types.</li>
|
||||
<li>Fix unfused loads of float fields.</li>
|
||||
<li>Fix <tt>ffi.string()</tt> recording.</li>
|
||||
<li>Save <tt>GetLastError()</tt> around <tt>ffi.load()</tt> and symbol
|
||||
resolving, too.</li>
|
||||
<li>Improve ld script detection in <tt>ffi.load()</tt>.</li>
|
||||
<li>Record loads/stores to external variables in namespaces.</li>
|
||||
<li>Compile calls to stdcall, fastcall and vararg functions.</li>
|
||||
<li>Treat function ctypes like pointers in comparisons.</li>
|
||||
<li>Resolve <tt>__call</tt> metamethod for pointers, too.</li>
|
||||
<li>Record C function calls with bool return values.</li>
|
||||
<li>Record <tt>ffi.errno()</tt>.</li>
|
||||
<li>x86: Fix number to <tt>uint32_t</tt> conversion rounding.</li>
|
||||
<li>x86: Fix 64 bit arithmetic in assembler backend.</li>
|
||||
<li>x64: Fix struct-by-value calling conventions.</li>
|
||||
<li>ARM: Ensure invocation of SPLIT pass for float conversions.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Display trace types with <tt>-jv</tt> and <tt>-jdump</tt>.</li>
|
||||
<li>Record isolated calls. But prefer recording loops over calls.</li>
|
||||
<li>Specialize to prototype for non-monomorphic functions. Solves the
|
||||
trace-explosion problem for closure-heavy programming styles.</li>
|
||||
<li>Always generate a portable <tt>vmdef.lua</tt>. Easier for distros.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta8">LuaJIT 2.0.0-beta8 — 2011-06-23</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>Soft-float ARM port of LuaJIT is complete.</li>
|
||||
<li>Add support for bytecode loading/saving and <tt>-b</tt> command line
|
||||
option.</li>
|
||||
<li>From Lua 5.2: <tt>__len</tt> metamethod for tables
|
||||
(disabled by default).</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>ARM: Misc. fixes for interpreter.</li>
|
||||
<li>x86/x64: Fix <tt>bit.*</tt> argument checking in interpreter.</li>
|
||||
<li>Catch early out-of-memory in memory allocator initialization.</li>
|
||||
<li>Fix data-flow analysis for paths leading to an upvalue close.</li>
|
||||
<li>Fix check for missing arguments in <tt>string.format()</tt>.</li>
|
||||
<li>Fix Solaris/x86 build (note: not a supported target).</li>
|
||||
<li>Fix recording of loops with instable directions in side traces.</li>
|
||||
<li>x86/x64: Fix fusion of comparisons with <tt>u8</tt>/<tt>u16</tt>
|
||||
<tt>XLOAD</tt>.</li>
|
||||
<li>x86/x64: Fix register allocation for variable shifts.</li>
|
||||
</ul></li>
|
||||
<li>FFI library:
|
||||
<ul>
|
||||
<li>Add <tt>ffi.errno()</tt>. Save <tt>errno</tt>/<tt>GetLastError()</tt>
|
||||
around allocations etc.</li>
|
||||
<li>Fix <tt>__gc</tt> for VLA/VLS cdata objects.</li>
|
||||
<li>Fix recording of casts from 32 bit cdata pointers to integers.</li>
|
||||
<li><tt>tonumber(cdata)</tt> returns <tt>nil</tt> for non-numbers.</li>
|
||||
<li>Show address pointed to for <tt>tostring(pointer)</tt>.</li>
|
||||
<li>Print <tt>NULL</tt> pointers as <tt>"cdata<... *>: NULL"</tt>.</li>
|
||||
<li>Support <tt>__tostring</tt> metamethod for pointers to structs, too.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>More tuning for loop unrolling heuristics.</li>
|
||||
<li>Flatten and compress in-memory debug info (saves ~70%).</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta7">LuaJIT 2.0.0-beta7 — 2011-05-05</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>ARM port of the LuaJIT interpreter is complete.</li>
|
||||
<li>FFI library: Add <tt>ffi.gc()</tt>, <tt>ffi.metatype()</tt>,
|
||||
<tt>ffi.istype()</tt>.</li>
|
||||
<li>FFI library: Resolve ld script redirection in <tt>ffi.load()</tt>.</li>
|
||||
<li>From Lua 5.2: <tt>package.searchpath()</tt>, <tt>fp:read("*L")</tt>,
|
||||
<tt>load(string)</tt>.</li>
|
||||
<li>From Lua 5.2, disabled by default: empty statement,
|
||||
<tt>table.unpack()</tt>, modified <tt>coroutine.running()</tt>.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>FFI library: numerous fixes.</li>
|
||||
<li>Fix type mismatches in store-to-load forwarding.</li>
|
||||
<li>Fix error handling within metamethods.</li>
|
||||
<li>Fix <tt>table.maxn()</tt>.</li>
|
||||
<li>Improve accuracy of <tt>x^-k</tt> on x64.</li>
|
||||
<li>Fix code generation for Intel Atom in x64 mode.</li>
|
||||
<li>Fix narrowing of POW.</li>
|
||||
<li>Fix recording of retried fast functions.</li>
|
||||
<li>Fix code generation for <tt>bit.bnot()</tt> and multiplies.</li>
|
||||
<li>Fix error location within cpcall frames.</li>
|
||||
<li>Add workaround for old libgcc unwind bug.</li>
|
||||
<li>Fix <tt>lua_yield()</tt> and <tt>getmetatable(lightuserdata)</tt> on x64.</li>
|
||||
<li>Misc. fixes for PPC/e500 interpreter.</li>
|
||||
<li>Fix stack slot updates for down-recursion.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Add dual-number mode (int/double) for the VM. Enabled for ARM.</li>
|
||||
<li>Improve narrowing of arithmetic operators and <tt>for</tt> loops.</li>
|
||||
<li>Tune loop unrolling heuristics and increase trace recorder limits.</li>
|
||||
<li>Eliminate dead slots in snapshots using bytecode data-flow analysis.</li>
|
||||
<li>Avoid phantom stores to proxy tables.</li>
|
||||
<li>Optimize lookups in empty proxy tables.</li>
|
||||
<li>Improve bytecode optimization of <tt>and</tt>/<tt>or</tt> operators.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta6">LuaJIT 2.0.0-beta6 — 2011-02-11</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>PowerPC/e500v2 port of the LuaJIT interpreter is complete.</li>
|
||||
<li>Various minor features from Lua 5.2: Hex escapes in literals,
|
||||
<tt>'\*'</tt> escape, reversible <tt>string.format("%q",s)</tt>,
|
||||
<tt>"%g"</tt> pattern, <tt>table.sort</tt> checks callbacks,
|
||||
<tt>os.exit(status|true|false[,close])</tt>.</li>
|
||||
<li>Lua 5.2 <tt>__pairs</tt> and <tt>__ipairs</tt> metamethods
|
||||
(disabled by default).</li>
|
||||
<li>Initial release of the FFI library.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix <tt>string.format()</tt> for non-finite numbers.</li>
|
||||
<li>Fix memory leak when compiled to use the built-in allocator.</li>
|
||||
<li>x86/x64: Fix unnecessary resize in <tt>TSETM</tt> bytecode.</li>
|
||||
<li>Fix various GC issues with traces and <tt>jit.flush()</tt>.</li>
|
||||
<li>x64: Fix fusion of indexes for array references.</li>
|
||||
<li>x86/x64: Fix stack overflow handling for coroutine results.</li>
|
||||
<li>Enable low-2GB memory allocation on FreeBSD/x64.</li>
|
||||
<li>Fix <tt>collectgarbage("count")</tt> result if more than 2GB is in use.</li>
|
||||
<li>Fix parsing of hex floats.</li>
|
||||
<li>x86/x64: Fix loop branch inversion with trailing
|
||||
<tt>HREF+NE/EQ</tt>.</li>
|
||||
<li>Add <tt>jit.os</tt> string.</li>
|
||||
<li><tt>coroutine.create()</tt> permits running C functions, too.</li>
|
||||
<li>Fix OSX build to work with newer ld64 versions.</li>
|
||||
<li>Fix bytecode optimization of <tt>and</tt>/<tt>or</tt> operators.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Emit specialized bytecode for <tt>pairs()</tt>/<tt>next()</tt>.</li>
|
||||
<li>Improve bytecode coalescing of <tt>nil</tt> constants.</li>
|
||||
<li>Compile calls to vararg functions.</li>
|
||||
<li>Compile <tt>select()</tt>.</li>
|
||||
<li>Improve alias analysis, esp. for loads from allocations.</li>
|
||||
<li>Tuning of various compiler heuristics.</li>
|
||||
<li>Refactor and extend IR conversion instructions.</li>
|
||||
<li>x86/x64: Various backend enhancements related to the FFI.</li>
|
||||
<li>Add SPLIT pass to split 64 bit IR instructions for 32 bit CPUs.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta5">LuaJIT 2.0.0-beta5 — 2010-08-24</h2>
|
||||
<ul>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix trace exit dispatch to function headers.</li>
|
||||
<li>Fix Windows and OSX builds with LUAJIT_DISABLE_JIT.</li>
|
||||
<li>Reorganize and fix placement of generated machine code on x64.</li>
|
||||
<li>Fix TNEW in x64 interpreter.</li>
|
||||
<li>Do not eliminate PHIs for values only referenced from side exits.</li>
|
||||
<li>OS-independent canonicalization of strings for non-finite numbers.</li>
|
||||
<li>Fix <tt>string.char()</tt> range check on x64.</li>
|
||||
<li>Fix <tt>tostring()</tt> resolving within <tt>print()</tt>.</li>
|
||||
<li>Fix error handling for <tt>next()</tt>.</li>
|
||||
<li>Fix passing of constant arguments to external calls on x64.</li>
|
||||
<li>Fix interpreter argument check for two-argument SSE math functions.</li>
|
||||
<li>Fix C frame chain corruption caused by <tt>lua_cpcall()</tt>.</li>
|
||||
<li>Fix return from <tt>pcall()</tt> within active hook.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Replace on-trace GC frame syncing with interpreter exit.</li>
|
||||
<li>Improve hash lookup specialization by not removing dead keys during GC.</li>
|
||||
<li>Turn traces into true GC objects.</li>
|
||||
<li>Avoid starting a GC cycle immediately after library init.</li>
|
||||
<li>Add weak guards to improve dead-code elimination.</li>
|
||||
<li>Speed up string interning.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta4">LuaJIT 2.0.0-beta4 — 2010-03-28</h2>
|
||||
<ul>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix precondition for on-trace creation of table keys.</li>
|
||||
<li>Fix <tt>{f()}</tt> on x64 when table is resized.</li>
|
||||
<li>Fix folding of ordered comparisons with same references.</li>
|
||||
<li>Fix snapshot restores for multi-result bytecodes.</li>
|
||||
<li>Fix potential hang when recording bytecode with nested closures.</li>
|
||||
<li>Fix recording of <tt>getmetatable()</tt>, <tt>tonumber()</tt> and bad argument types.</li>
|
||||
<li>Fix SLOAD fusion across returns to lower frames.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Add array bounds check elimination. <tt>-Oabc</tt> is enabled by default.</li>
|
||||
<li>More tuning for x64, e.g. smaller table objects.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta3">LuaJIT 2.0.0-beta3 — 2010-03-07</h2>
|
||||
<ul>
|
||||
<li>LuaJIT x64 port:
|
||||
<ul>
|
||||
<li>Port integrated memory allocator to Linux/x64, Windows/x64 and OSX/x64.</li>
|
||||
<li>Port interpreter and JIT compiler to x64.</li>
|
||||
<li>Port DynASM to x64.</li>
|
||||
<li>Many 32/64 bit cleanups in the VM.</li>
|
||||
<li>Allow building the interpreter with either x87 or SSE2 arithmetics.</li>
|
||||
<li>Add external unwinding and C++ exception interop (default on x64).</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix constructor bytecode generation for certain conditional values.</li>
|
||||
<li>Fix some cases of ordered string comparisons.</li>
|
||||
<li>Fix <tt>lua_tocfunction()</tt>.</li>
|
||||
<li>Fix cutoff register in JMP bytecode for some conditional expressions.</li>
|
||||
<li>Fix PHI marking algorithm for references from variant slots.</li>
|
||||
<li>Fix <tt>package.cpath</tt> for non-default PREFIX.</li>
|
||||
<li>Fix DWARF2 frame unwind information for interpreter on OSX.</li>
|
||||
<li>Drive the GC forward on string allocations in the parser.</li>
|
||||
<li>Implement call/return hooks (zero-cost if disabled).</li>
|
||||
<li>Implement yield from C hooks.</li>
|
||||
<li>Disable JIT compiler on older non-SSE2 CPUs instead of aborting.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Compile recursive code (tail-, up- and down-recursion).</li>
|
||||
<li>Improve heuristics for bytecode penalties and blacklisting.</li>
|
||||
<li>Split CALL/FUNC recording and clean up fast function call semantics.</li>
|
||||
<li>Major redesign of internal function call handling.</li>
|
||||
<li>Improve FOR loop const specialization and integerness checks.</li>
|
||||
<li>Switch to pre-initialized stacks. Avoid frame-clearing.</li>
|
||||
<li>Colocation of prototypes and related data: bytecode, constants, debug info.</li>
|
||||
<li>Cleanup parser and streamline bytecode generation.</li>
|
||||
<li>Add support for weak IR references to register allocator.</li>
|
||||
<li>Switch to compressed, extensible snapshots.</li>
|
||||
<li>Compile returns to frames below the start frame.</li>
|
||||
<li>Improve alias analysis of upvalues using a disambiguation hash value.</li>
|
||||
<li>Compile floor/ceil/trunc to SSE2 helper calls or SSE4.1 instructions.</li>
|
||||
<li>Add generic C call handling to IR and backend.</li>
|
||||
<li>Improve KNUM fuse vs. load heuristics.</li>
|
||||
<li>Compile various <tt>io.*()</tt> functions.</li>
|
||||
<li>Compile <tt>math.sinh()</tt>, <tt>math.cosh()</tt>, <tt>math.tanh()</tt>
|
||||
and <tt>math.random()</tt>.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta2">LuaJIT 2.0.0-beta2 — 2009-11-09</h2>
|
||||
<ul>
|
||||
<li>Reorganize build system. Build static+shared library on POSIX.</li>
|
||||
<li>Allow C++ exception conversion on all platforms
|
||||
using a wrapper function.</li>
|
||||
<li>Automatically catch C++ exceptions and rethrow Lua error
|
||||
(DWARF2 only).</li>
|
||||
<li>Check for the correct x87 FPU precision at strategic points.</li>
|
||||
<li>Always use wrappers for libm functions.</li>
|
||||
<li>Resurrect metamethod name strings before copying them.</li>
|
||||
<li>Mark current trace, even if compiler is idle.</li>
|
||||
<li>Ensure FILE metatable is created only once.</li>
|
||||
<li>Fix type comparisons when different integer types are involved.</li>
|
||||
<li>Fix <tt>getmetatable()</tt> recording.</li>
|
||||
<li>Fix TDUP with dead keys in template table.</li>
|
||||
<li><tt>jit.flush(tr)</tt> returns status.
|
||||
Prevent manual flush of a trace that's still linked.</li>
|
||||
<li>Improve register allocation heuristics for invariant references.</li>
|
||||
<li>Compile the push/pop variants of <tt>table.insert()</tt> and
|
||||
<tt>table.remove()</tt>.</li>
|
||||
<li>Compatibility with MSVC <tt>link /debug</tt>.</li>
|
||||
<li>Fix <tt>lua_iscfunction()</tt>.</li>
|
||||
<li>Fix <tt>math.random()</tt> when compiled with <tt>-fpic</tt> (OSX).</li>
|
||||
<li>Fix <tt>table.maxn()</tt>.</li>
|
||||
<li>Bump <tt>MACOSX_DEPLOYMENT_TARGET</tt> to <tt>10.4</tt></li>
|
||||
<li><tt>luaL_check*()</tt> and <tt>luaL_opt*()</tt> now support
|
||||
negative arguments, too.<br>
|
||||
This matches the behavior of Lua 5.1, but not the specification.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta1">LuaJIT 2.0.0-beta1 — 2009-10-31</h2>
|
||||
<ul>
|
||||
<li>This is the first public release of LuaJIT 2.0.</li>
|
||||
<li>The whole VM has been rewritten from the ground up, so there's
|
||||
no point in listing differences over earlier versions.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="major" style="background: #ffff80;">
|
||||
<h2 id="LuaJIT-1.1.8">LuaJIT 1.1.8 — 2012-04-16</h2>
|
||||
<ul>
|
||||
<li>Merged with Lua 5.1.5. Also integrated fixes for all
|
||||
<a href="http://www.lua.org/bugs.html#5.1.5"><span class="ext">»</span> <span class="ext">»</span> currently known bugs in Lua 5.1.5</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.7">LuaJIT 1.1.7 — 2011-05-05</h2>
|
||||
<ul>
|
||||
<li>Added fixes for the
|
||||
<a href="http://www.lua.org/bugs.html#5.1.4"><span class="ext">»</span> currently known bugs in Lua 5.1.4</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.6">LuaJIT 1.1.6 — 2010-03-28</h2>
|
||||
<ul>
|
||||
<li>Added fixes for the
|
||||
<a href="http://www.lua.org/bugs.html#5.1.4"><span class="ext">»</span> currently known bugs in Lua 5.1.4</a>.</li>
|
||||
<li>Removed wrong GC check in <tt>jit_createstate()</tt>.
|
||||
Thanks to Tim Mensch.</li>
|
||||
<li>Fixed bad assertions while compiling <tt>table.insert()</tt> and
|
||||
<tt>table.remove()</tt>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.5">LuaJIT 1.1.5 — 2008-10-25</h2>
|
||||
<ul>
|
||||
<li>Merged with Lua 5.1.4. Fixes all
|
||||
<a href="http://www.lua.org/bugs.html#5.1.3"><span class="ext">»</span> known bugs in Lua 5.1.3</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.4">LuaJIT 1.1.4 — 2008-02-05</h2>
|
||||
<ul>
|
||||
<li>Merged with Lua 5.1.3. Fixes all
|
||||
<a href="http://www.lua.org/bugs.html#5.1.2"><span class="ext">»</span> known bugs in Lua 5.1.2</a>.</li>
|
||||
<li>Fixed possible (but unlikely) stack corruption while compiling
|
||||
<tt>k^x</tt> expressions.</li>
|
||||
<li>Fixed DynASM template for cmpss instruction.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.3">LuaJIT 1.1.3 — 2007-05-24</h2>
|
||||
<ul>
|
||||
<li>Merged with Lua 5.1.2. Fixes all
|
||||
<a href="http://www.lua.org/bugs.html#5.1.1"><span class="ext">»</span> known bugs in Lua 5.1.1</a>.</li>
|
||||
<li>Merged pending Lua 5.1.x fixes: "return -nil" bug, spurious count hook call.</li>
|
||||
<li>Remove a (sometimes) wrong assertion in <tt>luaJIT_findpc()</tt>.</li>
|
||||
<li>DynASM now allows labels for displacements and <tt>.aword</tt>.</li>
|
||||
<li>Fix some compiler warnings for DynASM glue (internal API change).</li>
|
||||
<li>Correct naming for SSSE3 (temporarily known as SSE4) in DynASM and x86 disassembler.</li>
|
||||
<li>The loadable debug modules now handle redirection to stdout
|
||||
(e.g. <tt>-j trace=-</tt>).</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.2">LuaJIT 1.1.2 — 2006-06-24</h2>
|
||||
<ul>
|
||||
<li>Fix MSVC inline assembly: use only local variables with
|
||||
<tt>lua_number2int()</tt>.</li>
|
||||
<li>Fix "attempt to call a thread value" bug on Mac OS X:
|
||||
make values of consts used as lightuserdata keys unique
|
||||
to avoid joining by the compiler/linker.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.1">LuaJIT 1.1.1 — 2006-06-20</h2>
|
||||
<ul>
|
||||
<li>Merged with Lua 5.1.1. Fixes all
|
||||
<a href="http://www.lua.org/bugs.html#5.1"><span class="ext">»</span> known bugs in Lua 5.1</a>.</li>
|
||||
<li>Enforce (dynamic) linker error for EXE/DLL version mismatches.</li>
|
||||
<li>Minor changes to DynASM: faster pre-processing, smaller encoding
|
||||
for some immediates.</li>
|
||||
</ul>
|
||||
<p>
|
||||
This release is in sync with Coco 1.1.1 (see the
|
||||
<a href="http://coco.luajit.org/changes.html"><span class="ext">»</span> Coco Change History</a>).
|
||||
</p>
|
||||
|
||||
<h2 id="LuaJIT-1.1.0">LuaJIT 1.1.0 — 2006-03-13</h2>
|
||||
<ul>
|
||||
<li>Merged with Lua 5.1 (final).</li>
|
||||
|
||||
<li>New JIT call frame setup:
|
||||
<ul>
|
||||
<li>The C stack is kept 16 byte aligned (faster).
|
||||
Mandatory for Mac OS X on Intel, too.</li>
|
||||
<li>Faster calling conventions for internal C helper functions.</li>
|
||||
<li>Better instruction scheduling for function prologue, OP_CALL and
|
||||
OP_RETURN.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Miscellaneous optimizations:
|
||||
<ul>
|
||||
<li>Faster loads of FP constants. Remove narrow-to-wide store-to-load
|
||||
forwarding stalls.</li>
|
||||
<li>Use (scalar) SSE2 ops (if the CPU supports it) to speed up slot moves
|
||||
and FP to integer conversions.</li>
|
||||
<li>Optimized the two-argument form of <tt>OP_CONCAT</tt> (<tt>a..b</tt>).</li>
|
||||
<li>Inlined <tt>OP_MOD</tt> (<tt>a%b</tt>).
|
||||
With better accuracy than the C variant, too.</li>
|
||||
<li>Inlined <tt>OP_POW</tt> (<tt>a^b</tt>). Unroll <tt>x^k</tt> or
|
||||
use <tt>k^x = 2^(log2(k)*x)</tt> or call <tt>pow()</tt>.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Changes in the optimizer:
|
||||
<ul>
|
||||
<li>Improved hinting for table keys derived from table values
|
||||
(<tt>t1[t2[x]]</tt>).</li>
|
||||
<li>Lookup hinting now works with arbitrary object types and
|
||||
supports index chains, too.</li>
|
||||
<li>Generate type hints for arithmetic and comparison operators,
|
||||
OP_LEN, OP_CONCAT and OP_FORPREP.</li>
|
||||
<li>Remove several hint definitions in favour of a generic COMBINE hint.</li>
|
||||
<li>Complete rewrite of <tt>jit.opt_inline</tt> module
|
||||
(ex <tt>jit.opt_lib</tt>).</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Use adaptive deoptimization:
|
||||
<ul>
|
||||
<li>If runtime verification of a contract fails, the affected
|
||||
instruction is recompiled and patched on-the-fly.
|
||||
Regular programs will trigger deoptimization only occasionally.</li>
|
||||
<li>This avoids generating code for uncommon fallback cases
|
||||
most of the time. Generated code is up to 30% smaller compared to
|
||||
LuaJIT 1.0.3.</li>
|
||||
<li>Deoptimization is used for many opcodes and contracts:
|
||||
<ul>
|
||||
<li>OP_CALL, OP_TAILCALL: type mismatch for callable.</li>
|
||||
<li>Inlined calls: closure mismatch, parameter number and type mismatches.</li>
|
||||
<li>OP_GETTABLE, OP_SETTABLE: table or key type and range mismatches.</li>
|
||||
<li>All arithmetic and comparison operators, OP_LEN, OP_CONCAT,
|
||||
OP_FORPREP: operand type and range mismatches.</li>
|
||||
</ul></li>
|
||||
<li>Complete redesign of the debug and traceback info
|
||||
(bytecode ↔ mcode) to support deoptimization.
|
||||
Much more flexible and needs only 50% of the space.</li>
|
||||
<li>The modules <tt>jit.trace</tt>, <tt>jit.dumphints</tt> and
|
||||
<tt>jit.dump</tt> handle deoptimization.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Inlined many popular library functions
|
||||
(for commonly used arguments only):
|
||||
<ul>
|
||||
<li>Most <tt>math.*</tt> functions (the 18 most used ones)
|
||||
[2x-10x faster].</li>
|
||||
<li><tt>string.len</tt>, <tt>string.sub</tt> and <tt>string.char</tt>
|
||||
[2x-10x faster].</li>
|
||||
<li><tt>table.insert</tt>, <tt>table.remove</tt> and <tt>table.getn</tt>
|
||||
[3x-5x faster].</li>
|
||||
<li><tt>coroutine.yield</tt> and <tt>coroutine.resume</tt>
|
||||
[3x-5x faster].</li>
|
||||
<li><tt>pairs</tt>, <tt>ipairs</tt> and the corresponding iterators
|
||||
[8x-15x faster].</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Changes in the core and loadable modules and the stand-alone executable:
|
||||
<ul>
|
||||
<li>Added <tt>jit.version</tt>, <tt>jit.version_num</tt>
|
||||
and <tt>jit.arch</tt>.</li>
|
||||
<li>Reorganized some internal API functions (<tt>jit.util.*mcode*</tt>).</li>
|
||||
<li>The <tt>-j dump</tt> output now shows JSUB names, too.</li>
|
||||
<li>New x86 disassembler module written in pure Lua. No dependency
|
||||
on ndisasm anymore. Flexible API, very compact (500 lines)
|
||||
and complete (x87, MMX, SSE, SSE2, SSE3, SSSE3, privileged instructions).</li>
|
||||
<li><tt>luajit -v</tt> prints the LuaJIT version and copyright
|
||||
on a separate line.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Added SSE, SSE2, SSE3 and SSSE3 support to DynASM.</li>
|
||||
<li>Miscellaneous doc changes. Added a section about
|
||||
<a href="install.html#embedding">embedding LuaJIT</a>.</li>
|
||||
</ul>
|
||||
<p>
|
||||
This release is in sync with Coco 1.1.0 (see the
|
||||
<a href="http://coco.luajit.org/changes.html"><span class="ext">»</span> Coco Change History</a>).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="major" style="background: #ffffd0;">
|
||||
<h2 id="LuaJIT-1.0.3">LuaJIT 1.0.3 — 2005-09-08</h2>
|
||||
<ul>
|
||||
<li>Even more docs.</li>
|
||||
<li>Unified closure checks in <tt>jit.*</tt>.</li>
|
||||
<li>Fixed some range checks in <tt>jit.util.*</tt>.</li>
|
||||
<li>Fixed __newindex call originating from <tt>jit_settable_str()</tt>.</li>
|
||||
<li>Merged with Lua 5.1 alpha (including early bug fixes).</li>
|
||||
</ul>
|
||||
<p>
|
||||
This is the first public release of LuaJIT.
|
||||
</p>
|
||||
|
||||
<h2 id="LuaJIT-1.0.2">LuaJIT 1.0.2 — 2005-09-02</h2>
|
||||
<ul>
|
||||
<li>Add support for flushing the Valgrind translation cache <br>
|
||||
(<tt>MYCFLAGS= -DUSE_VALGRIND</tt>).</li>
|
||||
<li>Add support for freeing executable mcode memory to the <tt>mmap()</tt>-based
|
||||
variant for POSIX systems.</li>
|
||||
<li>Reorganized the C function signature handling in
|
||||
<tt>jit.opt_lib</tt>.</li>
|
||||
<li>Changed to index-based hints for inlining C functions.
|
||||
Still no support in the backend for inlining.</li>
|
||||
<li>Hardcode <tt>HEAP_CREATE_ENABLE_EXECUTE</tt> value if undefined.</li>
|
||||
<li>Misc. changes to the <tt>jit.*</tt> modules.</li>
|
||||
<li>Misc. changes to the Makefiles.</li>
|
||||
<li>Lots of new docs.</li>
|
||||
<li>Complete doc reorg.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Not released because Lua 5.1 alpha came out today.
|
||||
</p>
|
||||
|
||||
<h2 id="LuaJIT-1.0.1">LuaJIT 1.0.1 — 2005-08-31</h2>
|
||||
<ul>
|
||||
<li>Missing GC step in <tt>OP_CONCAT</tt>.</li>
|
||||
<li>Fix result handling for C –> JIT calls.</li>
|
||||
<li>Detect CPU feature bits.</li>
|
||||
<li>Encode conditional moves (<tt>fucomip</tt>) only when supported.</li>
|
||||
<li>Add fallback instructions for FP compares.</li>
|
||||
<li>Add support for <tt>LUA_COMPAT_VARARG</tt>. Still disabled by default.</li>
|
||||
<li>MSVC needs a specific place for the <tt>CALLBACK</tt> attribute
|
||||
(David Burgess).</li>
|
||||
<li>Misc. doc updates.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Interim non-public release.
|
||||
Special thanks to Adam D. Moss for reporting most of the bugs.
|
||||
</p>
|
||||
|
||||
<h2 id="LuaJIT-1.0.0">LuaJIT 1.0.0 — 2005-08-29</h2>
|
||||
<p>
|
||||
This is the initial non-public release of LuaJIT.
|
||||
</p>
|
||||
</div>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,102 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Contact</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Mike Pall">
|
||||
<meta name="Copyright" content="Copyright (C) 2005-2013, Mike Pall">
|
||||
<meta name="Language" content="en">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>Contact</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="status.html">Status</a>
|
||||
<ul><li>
|
||||
<a href="changes.html">Changes</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="faq.html">FAQ</a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
Please send general questions to the
|
||||
<a href="http://luajit.org/list.html"><span class="ext">»</span> LuaJIT mailing list</a>.
|
||||
You can also send any questions you have directly to me:
|
||||
</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
var xS="@-:\" .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ<abc>defghijklmnopqrstuvwxyz";function xD(s)
|
||||
{var len=s.length;var r="";for(var i=0;i<len;i++)
|
||||
{var c=s.charAt(i);var n=xS.indexOf(c);if(n!=-1)c=xS.charAt(69-n);r+=c;}
|
||||
document.write("<"+"p>"+r+"<"+"/p>\n");}
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
xD("fyZKB8xv\"FJytmz8.KAB0u52D")
|
||||
//--></script>
|
||||
<noscript>
|
||||
<p><img src="img/contact.png" alt="Contact info in image" width="170" height="13">
|
||||
</p>
|
||||
</noscript>
|
||||
|
||||
<h2>Copyright</h2>
|
||||
<p>
|
||||
All documentation is
|
||||
Copyright © 2005-2013 Mike Pall.
|
||||
</p>
|
||||
|
||||
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,407 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Extensions</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Mike Pall">
|
||||
<meta name="Copyright" content="Copyright (C) 2005-2013, Mike Pall">
|
||||
<meta name="Language" content="en">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
||||
<style type="text/css">
|
||||
table.exc {
|
||||
line-height: 1.2;
|
||||
}
|
||||
tr.exchead td {
|
||||
font-weight: bold;
|
||||
}
|
||||
td.excplatform {
|
||||
width: 48%;
|
||||
}
|
||||
td.exccompiler {
|
||||
width: 29%;
|
||||
}
|
||||
td.excinterop {
|
||||
width: 23%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>Extensions</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a class="current" href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="status.html">Status</a>
|
||||
<ul><li>
|
||||
<a href="changes.html">Changes</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="faq.html">FAQ</a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT is fully upwards-compatible with Lua 5.1. It supports all
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#5"><span class="ext">»</span> standard Lua
|
||||
library functions</a> and the full set of
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#3"><span class="ext">»</span> Lua/C API
|
||||
functions</a>.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT is also fully ABI-compatible to Lua 5.1 at the linker/dynamic
|
||||
loader level. This means you can compile a C module against the
|
||||
standard Lua headers and load the same shared library from either Lua
|
||||
or LuaJIT.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT extends the standard Lua VM with new functionality and adds
|
||||
several extension modules. Please note this page is only about
|
||||
<em>functional</em> enhancements and not about performance enhancements,
|
||||
such as the optimized VM, the faster interpreter or the JIT compiler.
|
||||
</p>
|
||||
|
||||
<h2 id="modules">Extensions Modules</h2>
|
||||
<p>
|
||||
LuaJIT comes with several built-in extension modules:
|
||||
</p>
|
||||
|
||||
<h3 id="bit"><tt>bit.*</tt> — Bitwise operations</h3>
|
||||
<p>
|
||||
LuaJIT supports all bitwise operations as defined by
|
||||
<a href="http://bitop.luajit.org"><span class="ext">»</span> Lua BitOp</a>:
|
||||
</p>
|
||||
<pre class="code">
|
||||
bit.tobit bit.tohex bit.bnot bit.band bit.bor bit.bxor
|
||||
bit.lshift bit.rshift bit.arshift bit.rol bit.ror bit.bswap
|
||||
</pre>
|
||||
<p>
|
||||
This module is a LuaJIT built-in — you don't need to download or
|
||||
install Lua BitOp. The Lua BitOp site has full documentation for all
|
||||
<a href="http://bitop.luajit.org/api.html"><span class="ext">»</span> Lua BitOp API functions</a>.
|
||||
</p>
|
||||
<p>
|
||||
Please make sure to <tt>require</tt> the module before using any of
|
||||
its functions:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local bit = require("bit")
|
||||
</pre>
|
||||
<p>
|
||||
An already installed Lua BitOp module is ignored by LuaJIT.
|
||||
This way you can use bit operations from both Lua and LuaJIT on a
|
||||
shared installation.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi"><tt>ffi.*</tt> — FFI library</h3>
|
||||
<p>
|
||||
The <a href="ext_ffi.html">FFI library</a> allows calling external
|
||||
C functions and the use of C data structures from pure Lua
|
||||
code.
|
||||
</p>
|
||||
|
||||
<h3 id="jit"><tt>jit.*</tt> — JIT compiler control</h3>
|
||||
<p>
|
||||
The functions in this module
|
||||
<a href="ext_jit.html">control the behavior of the JIT compiler engine</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="c_api">C API extensions</h3>
|
||||
<p>
|
||||
LuaJIT adds some
|
||||
<a href="ext_c_api.html">extra functions to the Lua/C API</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="library">Enhanced Standard Library Functions</h2>
|
||||
|
||||
<h3 id="xpcall"><tt>xpcall(f, err [,args...])</tt> passes arguments</h3>
|
||||
<p>
|
||||
Unlike the standard implementation in Lua 5.1, <tt>xpcall()</tt>
|
||||
passes any arguments after the error function to the function
|
||||
which is called in a protected context.
|
||||
</p>
|
||||
|
||||
<h3 id="load"><tt>loadfile()</tt> etc. handle UTF-8 source code</h3>
|
||||
<p>
|
||||
Non-ASCII characters are handled transparently by the Lua source code parser.
|
||||
This allows the use of UTF-8 characters in identifiers and strings.
|
||||
A UTF-8 BOM is skipped at the start of the source code.
|
||||
</p>
|
||||
|
||||
<h3 id="tostring"><tt>tostring()</tt> etc. canonicalize NaN and ±Inf</h3>
|
||||
<p>
|
||||
All number-to-string conversions consistently convert non-finite numbers
|
||||
to the same strings on all platforms. NaN results in <tt>"nan"</tt>,
|
||||
positive infinity results in <tt>"inf"</tt> and negative infinity results
|
||||
in <tt>"-inf"</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="tonumber"><tt>tonumber()</tt> etc. use builtin string to number conversion</h3>
|
||||
<p>
|
||||
All string-to-number conversions consistently convert integer and
|
||||
floating-point inputs in decimal and hexadecimal on all platforms.
|
||||
<tt>strtod()</tt> is <em>not</em> used anymore, which avoids numerous
|
||||
problems with poor C library implementations. The builtin conversion
|
||||
function provides full precision according to the IEEE-754 standard, it
|
||||
works independently of the current locale and it supports hex floating-point
|
||||
numbers (e.g. <tt>0x1.5p-3</tt>).
|
||||
</p>
|
||||
|
||||
<h3 id="string_dump"><tt>string.dump(f [,strip])</tt> generates portable bytecode</h3>
|
||||
<p>
|
||||
An extra argument has been added to <tt>string.dump()</tt>. If set to
|
||||
<tt>true</tt>, 'stripped' bytecode without debug information is
|
||||
generated. This speeds up later bytecode loading and reduces memory
|
||||
usage. See also the
|
||||
<a href="running.html#opt_b"><tt>-b</tt> command line option</a>.
|
||||
</p>
|
||||
<p>
|
||||
The generated bytecode is portable and can be loaded on any architecture
|
||||
that LuaJIT supports, independent of word size or endianess. However the
|
||||
bytecode compatibility versions must match. Bytecode stays compatible
|
||||
for dot releases (x.y.0 → x.y.1), but may change with major or
|
||||
minor releases (2.0 → 2.1) or between any beta release. Foreign
|
||||
bytecode (e.g. from Lua 5.1) is incompatible and cannot be loaded.
|
||||
</p>
|
||||
|
||||
<h3 id="math_random">Enhanced PRNG for <tt>math.random()</tt></h3>
|
||||
<p>
|
||||
LuaJIT uses a Tausworthe PRNG with period 2^223 to implement
|
||||
<tt>math.random()</tt> and <tt>math.randomseed()</tt>. The quality of
|
||||
the PRNG results is much superior compared to the standard Lua
|
||||
implementation which uses the platform-specific ANSI rand().
|
||||
</p>
|
||||
<p>
|
||||
The PRNG generates the same sequences from the same seeds on all
|
||||
platforms and makes use of all bits in the seed argument.
|
||||
<tt>math.random()</tt> without arguments generates 52 pseudo-random bits
|
||||
for every call. The result is uniformly distributed between 0.0 and 1.0.
|
||||
It's correctly scaled up and rounded for <tt>math.random(n [,m])</tt> to
|
||||
preserve uniformity.
|
||||
</p>
|
||||
|
||||
<h3 id="io"><tt>io.*</tt> functions handle 64 bit file offsets</h3>
|
||||
<p>
|
||||
The file I/O functions in the standard <tt>io.*</tt> library handle
|
||||
64 bit file offsets. In particular this means it's possible
|
||||
to open files larger than 2 Gigabytes and to reposition or obtain
|
||||
the current file position for offsets beyond 2 GB
|
||||
(<tt>fp:seek()</tt> method).
|
||||
</p>
|
||||
|
||||
<h3 id="debug_meta"><tt>debug.*</tt> functions identify metamethods</h3>
|
||||
<p>
|
||||
<tt>debug.getinfo()</tt> and <tt>lua_getinfo()</tt> also return information
|
||||
about invoked metamethods. The <tt>namewhat</tt> field is set to
|
||||
<tt>"metamethod"</tt> and the <tt>name</tt> field has the name of
|
||||
the corresponding metamethod (e.g. <tt>"__index"</tt>).
|
||||
</p>
|
||||
|
||||
<h2 id="resumable">Fully Resumable VM</h2>
|
||||
<p>
|
||||
The LuaJIT VM is fully resumable. This means you can yield from a
|
||||
coroutine even across contexts, where this would not possible with
|
||||
the standard Lua 5.1 VM: e.g. you can yield across <tt>pcall()</tt>
|
||||
and <tt>xpcall()</tt>, across iterators and across metamethods.
|
||||
</p>
|
||||
|
||||
<h2 id="lua52">Extensions from Lua 5.2</h2>
|
||||
<p>
|
||||
LuaJIT supports some language and library extensions from Lua 5.2.
|
||||
Features that are unlikely to break existing code are unconditionally
|
||||
enabled:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>goto</tt> and <tt>::labels::</tt>.</li>
|
||||
<li>Hex escapes <tt>'\x3F'</tt> and <tt>'\*'</tt> escape in strings.</li>
|
||||
<li><tt>load(string|reader, chunkname [,mode [,env]])</tt>.
|
||||
<tt>loadstring()</tt> is an alias.</li>
|
||||
<li><tt>math.log(x [,base])</tt>.
|
||||
<li><tt>string.rep(s, n [,sep])</tt>.
|
||||
<li><tt>string.format()</tt>: <tt>%q</tt> reversible.
|
||||
<tt>%s</tt> checks <tt>__tostring</tt>.
|
||||
<tt>%a</tt> and <tt>"%A</tt> added.</li>
|
||||
<li>String matching pattern <tt>%g</tt> added.</li>
|
||||
<li><tt>io.read("*L")</tt>.</li>
|
||||
<li><tt>io.lines()</tt> and <tt>file:lines()</tt> process
|
||||
<tt>io.read()</tt> options.</li>
|
||||
<li><tt>os.exit(status|true|false [,close])</tt>.</li>
|
||||
<li><tt>package.searchpath(name, path [, sep [, rep]])</tt>.</li>
|
||||
<li><tt>package.loadlib(name, "*")</tt>.</li>
|
||||
<li><tt>debug.getinfo()</tt> returns <tt>nparams</tt> and <tt>isvararg</tt>
|
||||
for option <tt>"u"</tt>.</li>
|
||||
<li><tt>debug.getlocal()</tt> accepts function instead of level.</li>
|
||||
<li><tt>debug.getlocal()</tt> and <tt>debug.setlocal()</tt> accept negative
|
||||
indexes for varargs.</li>
|
||||
<li><tt>debug.getupvalue()</tt> and <tt>debug.setupvalue()</tt> handle
|
||||
C functions.</li>
|
||||
<li><tt>debug.upvalueid()</tt> and <tt>debug.upvaluejoin()</tt>.</li>
|
||||
<li>Command line option <tt>-E</tt>.</li>
|
||||
<li>Command line checks <tt>__tostring</tt> for errors.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Other features are only enabled, if LuaJIT is built with
|
||||
<tt>-DLUAJIT_ENABLE_LUA52COMPAT</tt>:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>goto</tt> is a keyword and not a valid variable name anymore.</li>
|
||||
<li><tt>break</tt> can be placed anywhere. Empty statements (<tt>;;</tt>)
|
||||
are allowed.</li>
|
||||
<li><tt>__lt</tt>, <tt>__le</tt> are invoked for mixed types.</li>
|
||||
<li><tt>__len</tt> for tables. <tt>rawlen()</tt> library function.</li>
|
||||
<li><tt>pairs()</tt> and <tt>ipairs()</tt> check for <tt>__pairs</tt> and
|
||||
<tt>__ipairs</tt>.</li>
|
||||
<li><tt>coroutine.running()</tt> returns two results.</li>
|
||||
<li><tt>table.pack()</tt> and <tt>table.unpack()</tt>
|
||||
(same as <tt>unpack()</tt>).</li>
|
||||
<li><tt>io.write()</tt> and <tt>file:write()</tt> return file handle
|
||||
instead of <tt>true</tt>.</li>
|
||||
<li><tt>os.execute()</tt> and <tt>pipe:close()</tt> return detailed
|
||||
exit status.</li>
|
||||
<li><tt>debug.setmetatable()</tt> returns object.</li>
|
||||
<li><tt>debug.getuservalue()</tt> and <tt>debug.setuservalue()</tt>.</li>
|
||||
<li>Remove <tt>math.mod()</tt>, <tt>string.gfind()</tt>.
|
||||
</ul>
|
||||
<p>
|
||||
Note: this provides only partial compatibility with Lua 5.2 at the
|
||||
language and Lua library level. LuaJIT is API+ABI-compatible with
|
||||
Lua 5.1, which prevents implementing features that would otherwise
|
||||
break the Lua/C API and ABI (e.g. <tt>_ENV</tt>).
|
||||
</p>
|
||||
|
||||
<h2 id="exceptions">C++ Exception Interoperability</h2>
|
||||
<p>
|
||||
LuaJIT has built-in support for interoperating with C++ exceptions.
|
||||
The available range of features depends on the target platform and
|
||||
the toolchain used to compile LuaJIT:
|
||||
</p>
|
||||
<table class="exc">
|
||||
<tr class="exchead">
|
||||
<td class="excplatform">Platform</td>
|
||||
<td class="exccompiler">Compiler</td>
|
||||
<td class="excinterop">Interoperability</td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="excplatform">POSIX/x64, DWARF2 unwinding</td>
|
||||
<td class="exccompiler">GCC 4.3+</td>
|
||||
<td class="excinterop"><b style="color: #00a000;">Full</b></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="excplatform">Other platforms, DWARF2 unwinding</td>
|
||||
<td class="exccompiler">GCC</td>
|
||||
<td class="excinterop"><b style="color: #c06000;">Limited</b></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="excplatform">Windows/x64</td>
|
||||
<td class="exccompiler">MSVC or WinSDK</td>
|
||||
<td class="excinterop"><b style="color: #00a000;">Full</b></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="excplatform">Windows/x86</td>
|
||||
<td class="exccompiler">Any</td>
|
||||
<td class="excinterop"><b style="color: #a00000;">No</b></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="excplatform">Other platforms</td>
|
||||
<td class="exccompiler">Other compilers</td>
|
||||
<td class="excinterop"><b style="color: #a00000;">No</b></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
<b style="color: #00a000;">Full interoperability</b> means:
|
||||
</p>
|
||||
<ul>
|
||||
<li>C++ exceptions can be caught on the Lua side with <tt>pcall()</tt>,
|
||||
<tt>lua_pcall()</tt> etc.</li>
|
||||
<li>C++ exceptions will be converted to the generic Lua error
|
||||
<tt>"C++ exception"</tt>, unless you use the
|
||||
<a href="ext_c_api.html#mode_wrapcfunc">C call wrapper</a> feature.</li>
|
||||
<li>It's safe to throw C++ exceptions across non-protected Lua frames
|
||||
on the C stack. The contents of the C++ exception object
|
||||
pass through unmodified.</li>
|
||||
<li>Lua errors can be caught on the C++ side with <tt>catch(...)</tt>.
|
||||
The corresponding Lua error message can be retrieved from the Lua stack.</li>
|
||||
<li>Throwing Lua errors across C++ frames is safe. C++ destructors
|
||||
will be called.</li>
|
||||
</ul>
|
||||
<p>
|
||||
<b style="color: #c06000;">Limited interoperability</b> means:
|
||||
</p>
|
||||
<ul>
|
||||
<li>C++ exceptions can be caught on the Lua side with <tt>pcall()</tt>,
|
||||
<tt>lua_pcall()</tt> etc.</li>
|
||||
<li>C++ exceptions will be converted to the generic Lua error
|
||||
<tt>"C++ exception"</tt>, unless you use the
|
||||
<a href="ext_c_api.html#mode_wrapcfunc">C call wrapper</a> feature.</li>
|
||||
<li>C++ exceptions will be caught by non-protected Lua frames and
|
||||
are rethrown as a generic Lua error. The C++ exception object will
|
||||
be destroyed.</li>
|
||||
<li>Lua errors <b>cannot</b> be caught on the C++ side.</li>
|
||||
<li>Throwing Lua errors across C++ frames will <b>not</b> call
|
||||
C++ destructors.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<b style="color: #a00000;">No interoperability</b> means:
|
||||
</p>
|
||||
<ul>
|
||||
<li>It's <b>not</b> safe to throw C++ exceptions across Lua frames.</li>
|
||||
<li>C++ exceptions <b>cannot</b> be caught on the Lua side.</li>
|
||||
<li>Lua errors <b>cannot</b> be caught on the C++ side.</li>
|
||||
<li>Throwing Lua errors across C++ frames will <b>not</b> call
|
||||
C++ destructors.</li>
|
||||
<li>Additionally, on Windows/x86 with SEH-based C++ exceptions:
|
||||
it's <b>not</b> safe to throw a Lua error across any frames containing
|
||||
a C++ function with any try/catch construct or using variables with
|
||||
(implicit) destructors. This also applies to any functions which may be
|
||||
inlined in such a function. It doesn't matter whether <tt>lua_error()</tt>
|
||||
is called inside or outside of a try/catch or whether any object actually
|
||||
needs to be destroyed: the SEH chain is corrupted and this will eventually
|
||||
lead to the termination of the process.</li>
|
||||
</ul>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,184 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Frequently Asked Questions (FAQ)</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Mike Pall">
|
||||
<meta name="Copyright" content="Copyright (C) 2005-2013, Mike Pall">
|
||||
<meta name="Language" content="en">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
||||
<style type="text/css">
|
||||
dd { margin-left: 1.5em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>Frequently Asked Questions (FAQ)</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="status.html">Status</a>
|
||||
<ul><li>
|
||||
<a href="changes.html">Changes</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a class="current" href="faq.html">FAQ</a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<dl>
|
||||
<dt>Q: Where can I learn more about LuaJIT and Lua?</dt>
|
||||
<dd>
|
||||
<ul style="padding: 0;">
|
||||
<li>The <a href="http://luajit.org/list.html"><span class="ext">»</span> LuaJIT mailing list</a> focuses on topics
|
||||
related to LuaJIT.</li>
|
||||
<li>The <a href="http://wiki.luajit.org/"><span class="ext">»</span> LuaJIT wiki</a> gathers community
|
||||
resources about LuaJIT.</li>
|
||||
<li>News about Lua itself can be found at the
|
||||
<a href="http://www.lua.org/lua-l.html"><span class="ext">»</span> Lua mailing list</a>.
|
||||
The mailing list archives are worth checking out for older postings
|
||||
about LuaJIT.</li>
|
||||
<li>The <a href="http://lua.org"><span class="ext">»</span> main Lua.org site</a> has complete
|
||||
<a href="http://www.lua.org/docs.html"><span class="ext">»</span> documentation</a> of the language
|
||||
and links to books and papers about Lua.</li>
|
||||
<li>The community-managed <a href="http://lua-users.org/wiki/"><span class="ext">»</span> Lua Wiki</a>
|
||||
has information about diverse topics.</li>
|
||||
</ul>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: Where can I learn more about the compiler technology used by LuaJIT?</dt>
|
||||
<dd>
|
||||
I'm planning to write more documentation about the internals of LuaJIT.
|
||||
In the meantime, please use the following Google Scholar searches
|
||||
to find relevant papers:<br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=Trace+Compiler"><span class="ext">»</span> Trace Compiler</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=JIT+Compiler"><span class="ext">»</span> JIT Compiler</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=Dynamic+Language+Optimizations"><span class="ext">»</span> Dynamic Language Optimizations</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=SSA+Form"><span class="ext">»</span> SSA Form</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=Linear+Scan+Register+Allocation"><span class="ext">»</span> Linear Scan Register Allocation</a><br>
|
||||
Here is a list of the <a href="http://article.gmane.org/gmane.comp.lang.lua.general/58908"><span class="ext">»</span> innovative features in LuaJIT</a>.<br>
|
||||
And, you know, reading the source is of course the only way to enlightenment. :-)
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: Why do I get this error: "attempt to index global 'arg' (a nil value)"?<br>
|
||||
Q: My vararg functions fail after switching to LuaJIT!</dt>
|
||||
<dd>LuaJIT is compatible to the Lua 5.1 language standard. It doesn't
|
||||
support the implicit <tt>arg</tt> parameter for old-style vararg
|
||||
functions from Lua 5.0.<br>Please convert your code to the
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#2.5.9"><span class="ext">»</span> Lua 5.1
|
||||
vararg syntax</a>.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: Why do I get this error: "bad FPU precision"?<br>
|
||||
<dt>Q: I get weird behavior after initializing Direct3D.<br>
|
||||
<dt>Q: Some FPU operations crash after I load a Delphi DLL.<br>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
DirectX/Direct3D (up to version 9) sets the x87 FPU to single-precision
|
||||
mode by default. This violates the Windows ABI and interferes with the
|
||||
operation of many programs — LuaJIT is affected, too. Please make
|
||||
sure you always use the <tt>D3DCREATE_FPU_PRESERVE</tt> flag when
|
||||
initializing Direct3D.<br>
|
||||
|
||||
Direct3D version 10 or higher do not show this behavior anymore.
|
||||
Consider testing your application with older versions, too.<br>
|
||||
|
||||
Similarly, the Borland/Delphi runtime modifies the FPU control word and
|
||||
enables FP exceptions. Of course this violates the Windows ABI, too.
|
||||
Please check the Delphi docs for the Set8087CW method.
|
||||
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: Sometimes Ctrl-C fails to stop my Lua program. Why?</dt>
|
||||
<dd>The interrupt signal handler sets a Lua debug hook. But this is
|
||||
currently ignored by compiled code (this will eventually be fixed). If
|
||||
your program is running in a tight loop and never falls back to the
|
||||
interpreter, the debug hook never runs and can't throw the
|
||||
"interrupted!" error.<br> In the meantime you have to press Ctrl-C
|
||||
twice to get stop your program. That's similar to when it's stuck
|
||||
running inside a C function under the Lua interpreter.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: Why doesn't my favorite power-patch for Lua apply against LuaJIT?</dt>
|
||||
<dd>Because it's a completely redesigned VM and has very little code
|
||||
in common with Lua anymore. Also, if the patch introduces changes to
|
||||
the Lua semantics, these would need to be reflected everywhere in the
|
||||
VM, from the interpreter up to all stages of the compiler.<br> Please
|
||||
use only standard Lua language constructs. For many common needs you
|
||||
can use source transformations or use wrapper or proxy functions.
|
||||
The compiler will happily optimize away such indirections.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: Lua runs everywhere. Why doesn't LuaJIT support my CPU?</dt>
|
||||
<dd>Because it's a compiler — it needs to generate native
|
||||
machine code. This means the code generator must be ported to each
|
||||
architecture. And the fast interpreter is written in assembler and
|
||||
must be ported, too. This is quite an undertaking.<br>
|
||||
The <a href="install.html">install documentation</a> shows the supported
|
||||
architectures. Other architectures will follow based on sufficient user
|
||||
demand and/or sponsoring.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: When will feature X be added? When will the next version be released?</dt>
|
||||
<dd>When it's ready.<br>
|
||||
C'mon, it's open source — I'm doing it on my own time and you're
|
||||
getting it for free. You can either contribute a patch or sponsor
|
||||
the development of certain features, if they are important to you.
|
||||
</dd>
|
||||
</dl>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,613 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Installation</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Mike Pall">
|
||||
<meta name="Copyright" content="Copyright (C) 2005-2013, Mike Pall">
|
||||
<meta name="Language" content="en">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
||||
<style type="text/css">
|
||||
table.compat {
|
||||
line-height: 1.2;
|
||||
font-size: 80%;
|
||||
}
|
||||
table.compat td {
|
||||
border: 1px solid #bfcfff;
|
||||
height: 2.5em;
|
||||
}
|
||||
table.compat tr.compathead td {
|
||||
font-weight: bold;
|
||||
border-bottom: 2px solid #bfcfff;
|
||||
}
|
||||
tr.compathead td.compatos {
|
||||
vertical-align: top;
|
||||
}
|
||||
table.compat td.compatcpu {
|
||||
width: 18%;
|
||||
border-right: 2px solid #bfcfff;
|
||||
}
|
||||
td.compatos {
|
||||
width: 21%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
td.compatno {
|
||||
background-color: #d0d0d0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>Installation</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a class="current" href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="status.html">Status</a>
|
||||
<ul><li>
|
||||
<a href="changes.html">Changes</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="faq.html">FAQ</a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT is only distributed as a source package. This page explains
|
||||
how to build and install LuaJIT with different operating systems
|
||||
and C compilers.
|
||||
</p>
|
||||
<p>
|
||||
For the impatient (on POSIX systems):
|
||||
</p>
|
||||
<pre class="code">
|
||||
make && sudo make install
|
||||
</pre>
|
||||
<p>
|
||||
LuaJIT currently builds out-of-the box on most systems.
|
||||
Here's the compatibility matrix for the supported combinations of
|
||||
operating systems, CPUs and compilers:
|
||||
</p>
|
||||
<table class="compat">
|
||||
<tr class="compathead">
|
||||
<td class="compatcpu">CPU / OS</td>
|
||||
<td class="compatos"><a href="#posix">Linux</a> or<br><a href="#android">Android</a></td>
|
||||
<td class="compatos"><a href="#posix">*BSD, Other</a></td>
|
||||
<td class="compatos"><a href="#posix">OSX 10.4+</a> or<br><a href="#ios">iOS 3.0+</a></td>
|
||||
<td class="compatos"><a href="#windows">Windows<br>XP/Vista/7</a></td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="compatcpu">x86 (32 bit)</td>
|
||||
<td class="compatos">GCC 4.x<br>GCC 3.4</td>
|
||||
<td class="compatos">GCC 4.x<br>GCC 3.4</td>
|
||||
<td class="compatos">GCC 4.x<br>GCC 3.4</td>
|
||||
<td class="compatos">MSVC, MSVC/EE<br>WinSDK<br>MinGW, Cygwin</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="compatcpu">x64 (64 bit)</td>
|
||||
<td class="compatos">GCC 4.x</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos">GCC 4.x</td>
|
||||
<td class="compatos">MSVC + SDK v7.0<br>WinSDK v7.0</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="compatcpu"><a href="#cross2">ARMv5+<br>ARM9E+</a></td>
|
||||
<td class="compatos">GCC 4.2+</td>
|
||||
<td class="compatos">GCC 4.2+</td>
|
||||
<td class="compatos">GCC 4.2+</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="compatcpu"><a href="#cross2">PPC</a></td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos">GCC 4.3+<br>GCC 4.1 (<a href="#ps3">PS3</a>)</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos">XEDK (<a href="#xbox360">Xbox 360</a>)</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="compatcpu"><a href="#cross2">PPC/e500v2</a></td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos compatno"> </td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="compatcpu"><a href="#cross2">MIPS</a></td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos compatno"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Configuring LuaJIT</h2>
|
||||
<p>
|
||||
The standard configuration should work fine for most installations.
|
||||
Usually there is no need to tweak the settings. The following files
|
||||
hold all user-configurable settings:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>src/luaconf.h</tt> sets some configuration variables.</li>
|
||||
<li><tt>Makefile</tt> has settings for <b>installing</b> LuaJIT (POSIX
|
||||
only).</li>
|
||||
<li><tt>src/Makefile</tt> has settings for <b>compiling</b> LuaJIT
|
||||
under POSIX, MinGW or Cygwin.</li>
|
||||
<li><tt>src/msvcbuild.bat</tt> has settings for compiling LuaJIT with
|
||||
MSVC or WinSDK.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Please read the instructions given in these files, before changing
|
||||
any settings.
|
||||
</p>
|
||||
|
||||
<h2 id="posix">POSIX Systems (Linux, OSX, *BSD etc.)</h2>
|
||||
<h3>Prerequisites</h3>
|
||||
<p>
|
||||
Depending on your distribution, you may need to install a package for
|
||||
GCC, the development headers and/or a complete SDK. E.g. on a current
|
||||
Debian/Ubuntu, install <tt>libc6-dev</tt> with the package manager.
|
||||
</p>
|
||||
<p>
|
||||
Download the current source package of LuaJIT (pick the .tar.gz),
|
||||
if you haven't already done so. Move it to a directory of your choice,
|
||||
open a terminal window and change to this directory. Now unpack the archive
|
||||
and change to the newly created directory:
|
||||
</p>
|
||||
<pre class="code">
|
||||
tar zxf LuaJIT-2.0.1.tar.gz
|
||||
cd LuaJIT-2.0.1</pre>
|
||||
<h3>Building LuaJIT</h3>
|
||||
<p>
|
||||
The supplied Makefiles try to auto-detect the settings needed for your
|
||||
operating system and your compiler. They need to be run with GNU Make,
|
||||
which is probably the default on your system, anyway. Simply run:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make
|
||||
</pre>
|
||||
<p>
|
||||
This always builds a native x86, x64 or PPC binary, depending on the host OS
|
||||
you're running this command on. Check the section on
|
||||
<a href="#cross">cross-compilation</a> for more options.
|
||||
</p>
|
||||
<p>
|
||||
By default, modules are only searched under the prefix <tt>/usr/local</tt>.
|
||||
You can add an extra prefix to the search paths by appending the
|
||||
<tt>PREFIX</tt> option, e.g.:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make PREFIX=/home/myself/lj2
|
||||
</pre>
|
||||
<p>
|
||||
Note for OSX: if the <tt>MACOSX_DEPLOYMENT_TARGET</tt> environment
|
||||
variable is not set, then it's forced to <tt>10.4</tt>.
|
||||
</p>
|
||||
<h3>Installing LuaJIT</h3>
|
||||
<p>
|
||||
The top-level Makefile installs LuaJIT by default under
|
||||
<tt>/usr/local</tt>, i.e. the executable ends up in
|
||||
<tt>/usr/local/bin</tt> and so on. You need root privileges
|
||||
to write to this path. So, assuming sudo is installed on your system,
|
||||
run the following command and enter your sudo password:
|
||||
</p>
|
||||
<pre class="code">
|
||||
sudo make install
|
||||
</pre>
|
||||
<p>
|
||||
Otherwise specify the directory prefix as an absolute path, e.g.:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make install PREFIX=/home/myself/lj2
|
||||
</pre>
|
||||
<p>
|
||||
Obviously the prefixes given during build and installation need to be the same.
|
||||
</p>
|
||||
|
||||
<h2 id="windows">Windows Systems</h2>
|
||||
<h3>Prerequisites</h3>
|
||||
<p>
|
||||
Either install one of the open source SDKs
|
||||
(<a href="http://mingw.org/"><span class="ext">»</span> MinGW</a> or
|
||||
<a href="http://www.cygwin.com/"><span class="ext">»</span> Cygwin</a>), which come with a modified
|
||||
GCC plus the required development headers.
|
||||
</p>
|
||||
<p>
|
||||
Or install Microsoft's Visual C++ (MSVC). The freely downloadable
|
||||
<a href="http://www.microsoft.com/Express/VC/"><span class="ext">»</span> Express Edition</a>
|
||||
works just fine, but only contains an x86 compiler.
|
||||
</p>
|
||||
<p>
|
||||
The freely downloadable
|
||||
<a href="http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspx"><span class="ext">»</span> Windows SDK</a>
|
||||
only comes with command line tools, but this is all you need to build LuaJIT.
|
||||
It contains x86 and x64 compilers.
|
||||
</p>
|
||||
<p>
|
||||
Next, download the source package and unpack it using an archive manager
|
||||
(e.g. the Windows Explorer) to a directory of your choice.
|
||||
</p>
|
||||
<h3>Building with MSVC</h3>
|
||||
<p>
|
||||
Open a "Visual Studio .NET Command Prompt", <tt>cd</tt> to the
|
||||
directory where you've unpacked the sources and run these commands:
|
||||
</p>
|
||||
<pre class="code">
|
||||
cd src
|
||||
msvcbuild
|
||||
</pre>
|
||||
<p>
|
||||
Then follow the installation instructions below.
|
||||
</p>
|
||||
<h3>Building with the Windows SDK</h3>
|
||||
<p>
|
||||
Open a "Windows SDK Command Shell" and select the x86 compiler:
|
||||
</p>
|
||||
<pre class="code">
|
||||
setenv /release /x86
|
||||
</pre>
|
||||
<p>
|
||||
Or select the x64 compiler:
|
||||
</p>
|
||||
<pre class="code">
|
||||
setenv /release /x64
|
||||
</pre>
|
||||
<p>
|
||||
Then <tt>cd</tt> to the directory where you've unpacked the sources
|
||||
and run these commands:
|
||||
</p>
|
||||
<pre class="code">
|
||||
cd src
|
||||
msvcbuild
|
||||
</pre>
|
||||
<p>
|
||||
Then follow the installation instructions below.
|
||||
</p>
|
||||
<h3>Building with MinGW or Cygwin</h3>
|
||||
<p>
|
||||
Open a command prompt window and make sure the MinGW or Cygwin programs
|
||||
are in your path. Then <tt>cd</tt> to the directory where
|
||||
you've unpacked the sources and run this command for MinGW:
|
||||
</p>
|
||||
<pre class="code">
|
||||
mingw32-make
|
||||
</pre>
|
||||
<p>
|
||||
Or this command for Cygwin:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make
|
||||
</pre>
|
||||
<p>
|
||||
Then follow the installation instructions below.
|
||||
</p>
|
||||
<h3>Installing LuaJIT</h3>
|
||||
<p>
|
||||
Copy <tt>luajit.exe</tt> and <tt>lua51.dll</tt> (built in the <tt>src</tt>
|
||||
directory) to a newly created directory (any location is ok).
|
||||
Add <tt>lua</tt> and <tt>lua\jit</tt> directories below it and copy
|
||||
all Lua files from the <tt>src\jit</tt> directory of the distribution
|
||||
to the latter directory.
|
||||
</p>
|
||||
<p>
|
||||
There are no hardcoded
|
||||
absolute path names — all modules are loaded relative to the
|
||||
directory where <tt>luajit.exe</tt> is installed
|
||||
(see <tt>src/luaconf.h</tt>).
|
||||
</p>
|
||||
|
||||
<h2 id="cross">Cross-compiling LuaJIT</h2>
|
||||
<p>
|
||||
The GNU Makefile-based build system allows cross-compiling on any host
|
||||
for any supported target, as long as both architectures have the same
|
||||
pointer size. If you want to cross-compile to any 32 bit target on an
|
||||
x64 OS, you need to install the multilib development package (e.g.
|
||||
<tt>libc6-dev-i386</tt> on Debian/Ubuntu) and build a 32 bit host part
|
||||
(<tt>HOST_CC="gcc -m32"</tt>).
|
||||
</p>
|
||||
<p>
|
||||
You need to specify <tt>TARGET_SYS</tt> whenever the host OS and the
|
||||
target OS differ, or you'll get assembler or linker errors. E.g. if
|
||||
you're compiling on a Windows or OSX host for embedded Linux or Android,
|
||||
you need to add <tt>TARGET_SYS=Linux</tt> to the examples below. For a
|
||||
minimal target OS, you may need to disable the built-in allocator in
|
||||
<tt>src/Makefile</tt> and use <tt>TARGET_SYS=Other</tt>. The examples
|
||||
below only show some popular targets — please check the comments
|
||||
in <tt>src/Makefile</tt> for more details.
|
||||
</p>
|
||||
<pre class="code">
|
||||
# Cross-compile to a 32 bit binary on a multilib x64 OS
|
||||
make CC="gcc -m32"
|
||||
|
||||
# Cross-compile on Debian/Ubuntu for Windows (mingw32 package)
|
||||
make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows
|
||||
</pre>
|
||||
<p id="cross2">
|
||||
The <tt>CROSS</tt> prefix allows specifying a standard GNU cross-compile
|
||||
toolchain (Binutils, GCC and a matching libc). The prefix may vary
|
||||
depending on the <tt>--target</tt> the toolchain was built for (note the
|
||||
<tt>CROSS</tt> prefix has a trailing <tt>"-"</tt>). The examples below
|
||||
use the canonical toolchain triplets for Linux.
|
||||
</p>
|
||||
<p>
|
||||
Since there's often no easy way to detect CPU features at runtime, it's
|
||||
important to compile with the proper CPU or architecture settings. You
|
||||
can specify these when building the toolchain yourself. Or add
|
||||
<tt>-mcpu=...</tt> or <tt>-march=...</tt> to <tt>TARGET_CFLAGS</tt>. For
|
||||
ARM it's important to have the correct <tt>-mfloat-abi=...</tt> setting,
|
||||
too. Otherwise LuaJIT may not run at the full performance of your target
|
||||
CPU.
|
||||
</p>
|
||||
<pre class="code">
|
||||
# ARM soft-float
|
||||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabi- \
|
||||
TARGET_CFLAGS="-mfloat-abi=soft"
|
||||
|
||||
# ARM soft-float ABI with VFP (example for Cortex-A8)
|
||||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabi- \
|
||||
TARGET_CFLAGS="-mcpu=cortex-a8 -mfloat-abi=softfp"
|
||||
|
||||
# ARM hard-float ABI with VFP (armhf, requires recent toolchain)
|
||||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabihf-
|
||||
|
||||
# PPC
|
||||
make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu-
|
||||
# PPC/e500v2 (fast interpreter only)
|
||||
make HOST_CC="gcc -m32" CROSS=powerpc-e500v2-linux-gnuspe-
|
||||
|
||||
# MIPS big-endian
|
||||
make HOST_CC="gcc -m32" CROSS=mips-linux-
|
||||
# MIPS little-endian
|
||||
make HOST_CC="gcc -m32" CROSS=mipsel-linux-
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="android">Android</b> using the <a href="http://developer.android.com/sdk/ndk/index.html"><span class="ext">»</span> Android NDK</a>.
|
||||
The environment variables need to match the install locations and the
|
||||
desired target platform. E.g. Android 4.0 corresponds to ABI level 14.
|
||||
For details check the folder <tt>docs</tt> in the NDK directory.
|
||||
</p>
|
||||
<p>
|
||||
Only a few common variations for the different CPUs, ABIs and platforms
|
||||
are listed. Please use your own judgement for which combination you want
|
||||
to build/deploy or which lowest common denominator you want to pick:
|
||||
</p>
|
||||
<pre class="code">
|
||||
# Android/ARM, armeabi (ARMv5TE soft-float), Android 2.2+ (Froyo)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=8
|
||||
NDKVER=$NDK/toolchains/arm-linux-androideabi-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/arm-linux-androideabi-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-arm"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF"
|
||||
|
||||
# Android/ARM, armeabi-v7a (ARMv7 VFP), Android 4.0+ (ICS)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=14
|
||||
NDKVER=$NDK/toolchains/arm-linux-androideabi-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/arm-linux-androideabi-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-arm"
|
||||
NDKARCH="-march=armv7-a -mfloat-abi=softfp -Wl,--fix-cortex-a8"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF $NDKARCH"
|
||||
|
||||
# Android/MIPS, mips (MIPS32R1 hard-float), Android 4.0+ (ICS)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=14
|
||||
NDKVER=$NDK/toolchains/mipsel-linux-android-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/mipsel-linux-android-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-mips"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF"
|
||||
|
||||
# Android/x86, x86 (i686 SSE3), Android 4.0+ (ICS)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=14
|
||||
NDKVER=$NDK/toolchains/x86-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/i686-linux-android-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-x86"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF"
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="ios">iOS 3.0+</b> (iPhone/iPad) using the <a href="http://developer.apple.com/devcenter/ios/index.action"><span class="ext">»</span> iOS SDK</a>.
|
||||
The environment variables need to match the iOS SDK version:
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Note: <b>the JIT compiler is disabled for iOS</b>, because regular iOS Apps
|
||||
are not allowed to generate code at runtime. You'll only get the performance
|
||||
of the LuaJIT interpreter on iOS. This is still faster than plain Lua, but
|
||||
much slower than the JIT compiler. Please complain to Apple, not me.
|
||||
Or use Android. :-p
|
||||
</p>
|
||||
<pre class="code">
|
||||
IXCODE=`xcode-select -print-path`
|
||||
ISDK=$IXCODE/Platforms/iPhoneOS.platform/Developer
|
||||
ISDKVER=iPhoneOS6.0.sdk
|
||||
ISDKP=$ISDK/usr/bin/
|
||||
ISDKF="-arch armv7 -isysroot $ISDK/SDKs/$ISDKVER"
|
||||
make HOST_CC="gcc -m32 -arch i386" CROSS=$ISDKP TARGET_FLAGS="$ISDKF" \
|
||||
TARGET_SYS=iOS
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="ps3">PS3</b> using the PS3 SDK from
|
||||
a Linux host or a Windows host (requires 32 bit MinGW (GCC) on the host,
|
||||
too). Due to restrictions on consoles, the JIT compiler is disabled and
|
||||
only the fast interpreter is built:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make HOST_CC="gcc -m32" CROSS=ppu-lv2-
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="xbox360">Xbox 360</b> using the
|
||||
Xbox 360 SDK (MSVC + XEDK). Due to restrictions on consoles, the
|
||||
JIT compiler is disabled and only the fast interpreter is built.
|
||||
</p>
|
||||
<p>
|
||||
Open a "Visual Studio .NET Command Prompt" (32 bit host compiler),
|
||||
<tt>cd</tt> to the directory where you've unpacked the sources and run
|
||||
the following commands. This builds a static library <tt>luajit20.lib</tt>,
|
||||
which can be linked against your game, just like the Lua library.
|
||||
</p>
|
||||
<pre class="code">
|
||||
cd src
|
||||
xedkbuild
|
||||
</pre>
|
||||
|
||||
<h2 id="embed">Embedding LuaJIT</h2>
|
||||
<p>
|
||||
LuaJIT is API-compatible with Lua 5.1. If you've already embedded Lua
|
||||
into your application, you probably don't need to do anything to switch
|
||||
to LuaJIT, except link with a different library:
|
||||
</p>
|
||||
<ul>
|
||||
<li>It's strongly suggested to build LuaJIT separately using the supplied
|
||||
build system. Please do <em>not</em> attempt to integrate the individual
|
||||
source files into your build tree. You'll most likely get the internal build
|
||||
dependencies wrong or mess up the compiler flags. Treat LuaJIT like any
|
||||
other external library and link your application with either the dynamic
|
||||
or static library, depending on your needs.</li>
|
||||
<li>If you want to load C modules compiled for plain Lua
|
||||
with <tt>require()</tt>, you need to make sure the public symbols
|
||||
(e.g. <tt>lua_pushnumber</tt>) are exported, too:
|
||||
<ul><li>On POSIX systems you can either link to the shared library
|
||||
or link the static library into your application. In the latter case
|
||||
you'll need to export all public symbols from your main executable
|
||||
(e.g. <tt>-Wl,-E</tt> on Linux) and add the external dependencies
|
||||
(e.g. <tt>-lm -ldl</tt> on Linux).</li>
|
||||
<li>Since Windows symbols are bound to a specific DLL name, you need to
|
||||
link to the <tt>lua51.dll</tt> created by the LuaJIT build (do not rename
|
||||
the DLL). You may link LuaJIT statically on Windows only if you don't
|
||||
intend to load Lua/C modules at runtime.
|
||||
</li></ul>
|
||||
</li>
|
||||
<li>
|
||||
If you're building a 64 bit application on OSX which links directly or
|
||||
indirectly against LuaJIT, you need to link your main executable
|
||||
with these flags:
|
||||
<pre class="code">
|
||||
-pagezero_size 10000 -image_base 100000000
|
||||
</pre>
|
||||
Also, it's recommended to <tt>rebase</tt> all (self-compiled) shared libraries
|
||||
which are loaded at runtime on OSX/x64 (e.g. C extension modules for Lua).
|
||||
See: <tt>man rebase</tt>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Additional hints for initializing LuaJIT using the C API functions:</p>
|
||||
<ul>
|
||||
<li>Here's a
|
||||
<a href="http://lua-users.org/wiki/SimpleLuaApiExample"><span class="ext">»</span> simple example</a>
|
||||
for embedding Lua or LuaJIT into your application.</li>
|
||||
<li>Make sure you use <tt>luaL_newstate</tt>. Avoid using
|
||||
<tt>lua_newstate</tt>, since this uses the (slower) default memory
|
||||
allocator from your system (no support for this on x64).</li>
|
||||
<li>Make sure you use <tt>luaL_openlibs</tt> and not the old Lua 5.0 style
|
||||
of calling <tt>luaopen_base</tt> etc. directly.</li>
|
||||
<li>To change or extend the list of standard libraries to load, copy
|
||||
<tt>src/lib_init.c</tt> to your project and modify it accordingly.
|
||||
Make sure the <tt>jit</tt> library is loaded or the JIT compiler
|
||||
will not be activated.</li>
|
||||
<li>The <tt>bit.*</tt> module for bitwise operations
|
||||
is already built-in. There's no need to statically link
|
||||
<a href="http://bitop.luajit.org/"><span class="ext">»</span> Lua BitOp</a> to your application.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="distro">Hints for Distribution Maintainers</h2>
|
||||
<p>
|
||||
The LuaJIT build system has extra provisions for the needs of most
|
||||
POSIX-based distributions. If you're a package maintainer for
|
||||
a distribution, <em>please</em> make use of these features and
|
||||
avoid patching, subverting, autotoolizing or messing up the build system
|
||||
in unspeakable ways.
|
||||
</p>
|
||||
<p>
|
||||
There should be absolutely no need to patch <tt>luaconf.h</tt> or any
|
||||
of the Makefiles. And please do not hand-pick files for your packages —
|
||||
simply use whatever <tt>make install</tt> creates. There's a reason
|
||||
for all of the files <em>and</em> directories it creates.
|
||||
</p>
|
||||
<p>
|
||||
The build system uses GNU make and auto-detects most settings based on
|
||||
the host you're building it on. This should work fine for native builds,
|
||||
even when sandboxed. You may need to pass some of the following flags to
|
||||
<em>both</em> the <tt>make</tt> and the <tt>make install</tt> command lines
|
||||
for a regular distribution build:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>PREFIX</tt> overrides the installation path and should usually
|
||||
be set to <tt>/usr</tt>. Setting this also changes the module paths and
|
||||
the <tt>-rpath</tt> of the shared library.</li>
|
||||
<li><tt>DESTDIR</tt> is an absolute path which allows you to install
|
||||
to a shadow tree instead of the root tree of the build system.</li>
|
||||
<li>Have a look at the top-level <tt>Makefile</tt> and <tt>src/Makefile</tt>
|
||||
for additional variables to tweak. The following variables <em>may</em> be
|
||||
overridden, but it's <em>not</em> recommended, except for special needs
|
||||
like cross-builds:
|
||||
<tt>BUILDMODE, CC, HOST_CC, STATIC_CC, DYNAMIC_CC, CFLAGS, HOST_CFLAGS,
|
||||
TARGET_CFLAGS, LDFLAGS, HOST_LDFLAGS, TARGET_LDFLAGS, TARGET_SHLDFLAGS,
|
||||
TARGET_FLAGS, LIBS, HOST_LIBS, TARGET_LIBS, CROSS, HOST_SYS, TARGET_SYS
|
||||
</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
The build system has a special target for an amalgamated build, i.e.
|
||||
<tt>make amalg</tt>. This compiles the LuaJIT core as one huge C file
|
||||
and allows GCC to generate faster and shorter code. Alas, this requires
|
||||
lots of memory during the build. This may be a problem for some users,
|
||||
that's why it's not enabled by default. But it shouldn't be a problem for
|
||||
most build farms. It's recommended that binary distributions use this
|
||||
target for their LuaJIT builds.
|
||||
</p>
|
||||
<p>
|
||||
The tl;dr version of the above:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make amalg PREFIX=/usr && \
|
||||
make install PREFIX=/usr DESTDIR=/tmp/buildroot
|
||||
</pre>
|
||||
<p>
|
||||
Finally, if you encounter any difficulties, please
|
||||
<a href="contact.html">contact me</a> first, instead of releasing a broken
|
||||
package onto unsuspecting users. Because they'll usually gonna complain
|
||||
to me (the upstream) and not you (the package maintainer), anyway.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,228 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>LuaJIT</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Mike Pall">
|
||||
<meta name="Copyright" content="Copyright (C) 2005-2013, Mike Pall">
|
||||
<meta name="Language" content="en">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
||||
<meta name="description" content="LuaJIT is a Just-In-Time (JIT) compiler for the Lua language.">
|
||||
<style type="text/css">
|
||||
table.feature {
|
||||
width: inherit;
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
}
|
||||
table.feature td {
|
||||
width: 80px;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border: 4px solid #e6ecff;
|
||||
border-radius: 12px;
|
||||
}
|
||||
table.os td {
|
||||
background: #7080d0;
|
||||
background-image: linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -moz-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -webkit-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -o-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -ms-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
}
|
||||
table.os1 td {
|
||||
color: #ffff80;
|
||||
}
|
||||
table.os2 td {
|
||||
color: #ffa040;
|
||||
}
|
||||
table.compiler td {
|
||||
color: #2080ff;
|
||||
background: #62bf41;
|
||||
background-image: linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -moz-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -webkit-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -o-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -ms-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
}
|
||||
table.cpu td {
|
||||
color: #ffff00;
|
||||
background: #cf7251;
|
||||
background-image: linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -moz-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -webkit-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -o-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -ms-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
}
|
||||
table.fcompat td {
|
||||
color: #2060e0;
|
||||
background: #61cfcf;
|
||||
background-image: linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -moz-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -webkit-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -o-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -ms-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
}
|
||||
table.stats td {
|
||||
color: #ffffff;
|
||||
background: #a0a0a0;
|
||||
background-image: linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -moz-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -webkit-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -o-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -ms-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
}
|
||||
table.stats td.speed {
|
||||
color: #ff4020;
|
||||
}
|
||||
table.stats td.kb {
|
||||
color: #ffff80;
|
||||
background: #808080;
|
||||
background-image: linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -moz-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -webkit-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -o-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -ms-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
}
|
||||
table.feature small {
|
||||
font-size: 50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>LuaJIT</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a class="current" href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="status.html">Status</a>
|
||||
<ul><li>
|
||||
<a href="changes.html">Changes</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="faq.html">FAQ</a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT is a <b>Just-In-Time Compiler</b> (JIT) for the
|
||||
<a href="http://www.lua.org/"><span class="ext">»</span> Lua</a> programming language.
|
||||
Lua is a powerful, dynamic and light-weight programming language.
|
||||
It may be embedded or used as a general-purpose, stand-alone language.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT is Copyright © 2005-2013 Mike Pall, released under the
|
||||
<a href="http://www.opensource.org/licenses/mit-license.php"><span class="ext">»</span> MIT open source license</a>.
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
|
||||
<h2>Compatibility</h2>
|
||||
<table class="feature os os1">
|
||||
<tr><td>Windows</td><td>Linux</td><td>BSD</td><td>OSX</td><td>POSIX</td></tr>
|
||||
</table>
|
||||
<table class="feature os os2">
|
||||
<tr><td><span style="font-size:90%;">Embedded</span></td><td>Android</td><td>iOS</td><td>PS3</td><td>Xbox 360</td></tr>
|
||||
</table>
|
||||
<table class="feature compiler">
|
||||
<tr><td>GCC</td><td>CLANG<br>LLVM</td><td>MSVC</td></tr>
|
||||
</table>
|
||||
<table class="feature cpu">
|
||||
<tr><td>x86</td><td>x64</td><td>ARM</td><td>PPC</td><td>e500</td><td>MIPS</td></tr>
|
||||
</table>
|
||||
<table class="feature fcompat">
|
||||
<tr><td>Lua 5.1<br>API+ABI</td><td>+ JIT</td><td>+ BitOp</td><td>+ FFI</td><td>Drop-in<br>DLL/.so</td></tr>
|
||||
</table>
|
||||
|
||||
<h2>Overview</h2>
|
||||
<table class="feature stats">
|
||||
<tr>
|
||||
<td class="speed">3x<br>- 100x</td>
|
||||
<td class="kb">115 <small>KB</small><br>VM</td>
|
||||
<td class="kb">90 <small>KB</small><br>JIT</td>
|
||||
<td class="kloc">63 <small>KLOC</small><br>C</td>
|
||||
<td class="kloc">24 <small>KLOC</small><br>ASM</td>
|
||||
<td class="kloc">11 <small>KLOC</small><br>Lua</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p style="margin-top: 1em;">
|
||||
LuaJIT has been successfully used as a <b>scripting middleware</b> in
|
||||
games, appliances, network and graphics apps, numerical simulations,
|
||||
trading platforms and many other specialty applications. It scales from
|
||||
embedded devices, smartphones, desktops up to server farms. It combines
|
||||
high flexibility with <a href="http://luajit.org/performance.html"><span class="ext">»</span> high performance</a>
|
||||
and an unmatched <b>low memory footprint</b>.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT has been in continuous development since 2005. It's widely
|
||||
considered to be <b>one of the fastest dynamic language
|
||||
implementations</b>. It has outperformed other dynamic languages on many
|
||||
cross-language benchmarks since its first release — often by a
|
||||
substantial margin.
|
||||
</p>
|
||||
<p>
|
||||
For <b>LuaJIT 2.0</b>, the whole VM has been rewritten from the ground up
|
||||
and relentlessly optimized for performance. It combines a <b>high-speed
|
||||
interpreter</b>, written in assembler, with a <b>state-of-the-art JIT
|
||||
compiler</b>.
|
||||
</p>
|
||||
<p>
|
||||
An innovative <b>trace compiler</b> is integrated with advanced,
|
||||
SSA-based optimizations and highly tuned code generation backends.
|
||||
A substantial reduction of the overhead associated with dynamic languages
|
||||
allows it to break into the performance range traditionally reserved for
|
||||
offline, static language compilers.
|
||||
</p>
|
||||
|
||||
<h2>More ...</h2>
|
||||
<p>
|
||||
Please select a sub-topic in the navigation bar to learn more about LuaJIT.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,125 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Status & Roadmap</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta name="Author" content="Mike Pall">
|
||||
<meta name="Copyright" content="Copyright (C) 2005-2013, Mike Pall">
|
||||
<meta name="Language" content="en">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
|
||||
<style type="text/css">
|
||||
ul li { padding-bottom: 0.3em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>Status & Roadmap</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a class="current" href="status.html">Status</a>
|
||||
<ul><li>
|
||||
<a href="changes.html">Changes</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="faq.html">FAQ</a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
<span style="color: #0000c0;">LuaJIT 2.0</span> is the current
|
||||
<span style="color: #0000c0;">stable branch</span>. This branch is in
|
||||
feature-freeze — new features will only be added to LuaJIT 2.1.
|
||||
</p>
|
||||
|
||||
<h2>Current Status</h2>
|
||||
<p>
|
||||
LuaJIT ought to run all Lua 5.1-compatible source code just fine.
|
||||
It's considered a serious bug if the VM crashes or produces unexpected
|
||||
results — please report this.
|
||||
</p>
|
||||
<p>
|
||||
Known incompatibilities and issues in LuaJIT 2.0:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
There are some differences in <b>implementation-defined</b> behavior.
|
||||
These either have a good reason, are arbitrary design choices
|
||||
or are due to quirks in the VM. The latter cases may get fixed if a
|
||||
demonstrable need is shown.
|
||||
</li>
|
||||
<li>
|
||||
The Lua <b>debug API</b> is missing a couple of features (return
|
||||
hooks for non-Lua functions) and shows slightly different behavior
|
||||
in LuaJIT (no per-coroutine hooks, no tail call counting).
|
||||
</li>
|
||||
<li>
|
||||
Some checks are missing in the JIT-compiled code for obscure situations
|
||||
with <b>open upvalues aliasing</b> one of the SSA slots later on (or
|
||||
vice versa). Bonus points, if you can find a real world test case for
|
||||
this.
|
||||
</li>
|
||||
<li>
|
||||
Currently some <b>out-of-memory</b> errors from <b>on-trace code</b> are not
|
||||
handled correctly. The error may fall through an on-trace
|
||||
<tt>pcall</tt> or it may be passed on to the function set with
|
||||
<tt>lua_atpanic</tt> on x64. This issue will be fixed with the new
|
||||
garbage collector.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Roadmap</h2>
|
||||
<p>
|
||||
Please refer to the
|
||||
<a href="http://www.freelists.org/post/luajit/LuaJIT-Roadmap-20122013"><span class="ext">»</span> LuaJIT Roadmap 2012/2013</a> and an
|
||||
<a href="http://www.freelists.org/post/luajit/LuaJIT-Roadmap-20122013-UPDATE"><span class="ext">»</span> update on release planning</a> for details.
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,953 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM MIPS module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Module information:
|
||||
local _info = {
|
||||
arch = "mips",
|
||||
description = "DynASM MIPS module",
|
||||
version = "1.3.0",
|
||||
vernum = 10300,
|
||||
release = "2012-01-23",
|
||||
author = "Mike Pall",
|
||||
license = "MIT",
|
||||
}
|
||||
|
||||
-- Exported glue functions for the arch-specific module.
|
||||
local _M = { _info = _info }
|
||||
|
||||
-- Cache library functions.
|
||||
local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs
|
||||
local assert, setmetatable = assert, setmetatable
|
||||
local _s = string
|
||||
local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
|
||||
local match, gmatch = _s.match, _s.gmatch
|
||||
local concat, sort = table.concat, table.sort
|
||||
local bit = bit or require("bit")
|
||||
local band, shl, sar, tohex = bit.band, bit.lshift, bit.arshift, bit.tohex
|
||||
|
||||
-- Inherited tables and callbacks.
|
||||
local g_opt, g_arch
|
||||
local wline, werror, wfatal, wwarn
|
||||
|
||||
-- Action name list.
|
||||
-- CHECK: Keep this in sync with the C code!
|
||||
local action_names = {
|
||||
"STOP", "SECTION", "ESC", "REL_EXT",
|
||||
"ALIGN", "REL_LG", "LABEL_LG",
|
||||
"REL_PC", "LABEL_PC", "IMM",
|
||||
}
|
||||
|
||||
-- Maximum number of section buffer positions for dasm_put().
|
||||
-- CHECK: Keep this in sync with the C code!
|
||||
local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines.
|
||||
|
||||
-- Action name -> action number.
|
||||
local map_action = {}
|
||||
for n,name in ipairs(action_names) do
|
||||
map_action[name] = n-1
|
||||
end
|
||||
|
||||
-- Action list buffer.
|
||||
local actlist = {}
|
||||
|
||||
-- Argument list for next dasm_put(). Start with offset 0 into action list.
|
||||
local actargs = { 0 }
|
||||
|
||||
-- Current number of section buffer positions for dasm_put().
|
||||
local secpos = 1
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump action names and numbers.
|
||||
local function dumpactions(out)
|
||||
out:write("DynASM encoding engine action codes:\n")
|
||||
for n,name in ipairs(action_names) do
|
||||
local num = map_action[name]
|
||||
out:write(format(" %-10s %02X %d\n", name, num, num))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write action list buffer as a huge static C array.
|
||||
local function writeactions(out, name)
|
||||
local nn = #actlist
|
||||
if nn == 0 then nn = 1; actlist[0] = map_action.STOP end
|
||||
out:write("static const unsigned int ", name, "[", nn, "] = {\n")
|
||||
for i = 1,nn-1 do
|
||||
assert(out:write("0x", tohex(actlist[i]), ",\n"))
|
||||
end
|
||||
assert(out:write("0x", tohex(actlist[nn]), "\n};\n\n"))
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Add word to action list.
|
||||
local function wputxw(n)
|
||||
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||
actlist[#actlist+1] = n
|
||||
end
|
||||
|
||||
-- Add action to list with optional arg. Advance buffer pos, too.
|
||||
local function waction(action, val, a, num)
|
||||
local w = assert(map_action[action], "bad action name `"..action.."'")
|
||||
wputxw(0xff000000 + w * 0x10000 + (val or 0))
|
||||
if a then actargs[#actargs+1] = a end
|
||||
if a or num then secpos = secpos + (num or 1) end
|
||||
end
|
||||
|
||||
-- Flush action list (intervening C code or buffer pos overflow).
|
||||
local function wflush(term)
|
||||
if #actlist == actargs[1] then return end -- Nothing to flush.
|
||||
if not term then waction("STOP") end -- Terminate action list.
|
||||
wline(format("dasm_put(Dst, %s);", concat(actargs, ", ")), true)
|
||||
actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put().
|
||||
secpos = 1 -- The actionlist offset occupies a buffer position, too.
|
||||
end
|
||||
|
||||
-- Put escaped word.
|
||||
local function wputw(n)
|
||||
if n >= 0xff000000 then waction("ESC") end
|
||||
wputxw(n)
|
||||
end
|
||||
|
||||
-- Reserve position for word.
|
||||
local function wpos()
|
||||
local pos = #actlist+1
|
||||
actlist[pos] = ""
|
||||
return pos
|
||||
end
|
||||
|
||||
-- Store word to reserved position.
|
||||
local function wputpos(pos, n)
|
||||
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||
actlist[pos] = n
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Global label name -> global label number. With auto assignment on 1st use.
|
||||
local next_global = 20
|
||||
local map_global = setmetatable({}, { __index = function(t, name)
|
||||
if not match(name, "^[%a_][%w_]*$") then werror("bad global label") end
|
||||
local n = next_global
|
||||
if n > 2047 then werror("too many global labels") end
|
||||
next_global = n + 1
|
||||
t[name] = n
|
||||
return n
|
||||
end})
|
||||
|
||||
-- Dump global labels.
|
||||
local function dumpglobals(out, lvl)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("Global labels:\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(format(" %s\n", t[i]))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write global label enum.
|
||||
local function writeglobals(out, prefix)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("enum {\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(" ", prefix, t[i], ",\n")
|
||||
end
|
||||
out:write(" ", prefix, "_MAX\n};\n")
|
||||
end
|
||||
|
||||
-- Write global label names.
|
||||
local function writeglobalnames(out, name)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(" \"", t[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Extern label name -> extern label number. With auto assignment on 1st use.
|
||||
local next_extern = 0
|
||||
local map_extern_ = {}
|
||||
local map_extern = setmetatable({}, { __index = function(t, name)
|
||||
-- No restrictions on the name for now.
|
||||
local n = next_extern
|
||||
if n > 2047 then werror("too many extern labels") end
|
||||
next_extern = n + 1
|
||||
t[name] = n
|
||||
map_extern_[n] = name
|
||||
return n
|
||||
end})
|
||||
|
||||
-- Dump extern labels.
|
||||
local function dumpexterns(out, lvl)
|
||||
out:write("Extern labels:\n")
|
||||
for i=0,next_extern-1 do
|
||||
out:write(format(" %s\n", map_extern_[i]))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write extern label names.
|
||||
local function writeexternnames(out, name)
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=0,next_extern-1 do
|
||||
out:write(" \"", map_extern_[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Arch-specific maps.
|
||||
local map_archdef = { sp="r29", ra="r31" } -- Ext. register name -> int. name.
|
||||
|
||||
local map_type = {} -- Type name -> { ctype, reg }
|
||||
local ctypenum = 0 -- Type number (for Dt... macros).
|
||||
|
||||
-- Reverse defines for registers.
|
||||
function _M.revdef(s)
|
||||
if s == "r29" then return "sp"
|
||||
elseif s == "r31" then return "ra" end
|
||||
return s
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Template strings for MIPS instructions.
|
||||
local map_op = {
|
||||
-- First-level opcodes.
|
||||
j_1 = "08000000J",
|
||||
jal_1 = "0c000000J",
|
||||
b_1 = "10000000B",
|
||||
beqz_2 = "10000000SB",
|
||||
beq_3 = "10000000STB",
|
||||
bnez_2 = "14000000SB",
|
||||
bne_3 = "14000000STB",
|
||||
blez_2 = "18000000SB",
|
||||
bgtz_2 = "1c000000SB",
|
||||
addi_3 = "20000000TSI",
|
||||
li_2 = "24000000TI",
|
||||
addiu_3 = "24000000TSI",
|
||||
slti_3 = "28000000TSI",
|
||||
sltiu_3 = "2c000000TSI",
|
||||
andi_3 = "30000000TSU",
|
||||
lu_2 = "34000000TU",
|
||||
ori_3 = "34000000TSU",
|
||||
xori_3 = "38000000TSU",
|
||||
lui_2 = "3c000000TU",
|
||||
beqzl_2 = "50000000SB",
|
||||
beql_3 = "50000000STB",
|
||||
bnezl_2 = "54000000SB",
|
||||
bnel_3 = "54000000STB",
|
||||
blezl_2 = "58000000SB",
|
||||
bgtzl_2 = "5c000000SB",
|
||||
lb_2 = "80000000TO",
|
||||
lh_2 = "84000000TO",
|
||||
lwl_2 = "88000000TO",
|
||||
lw_2 = "8c000000TO",
|
||||
lbu_2 = "90000000TO",
|
||||
lhu_2 = "94000000TO",
|
||||
lwr_2 = "98000000TO",
|
||||
sb_2 = "a0000000TO",
|
||||
sh_2 = "a4000000TO",
|
||||
swl_2 = "a8000000TO",
|
||||
sw_2 = "ac000000TO",
|
||||
swr_2 = "b8000000TO",
|
||||
cache_2 = "bc000000NO",
|
||||
ll_2 = "c0000000TO",
|
||||
lwc1_2 = "c4000000HO",
|
||||
pref_2 = "cc000000NO",
|
||||
ldc1_2 = "d4000000HO",
|
||||
sc_2 = "e0000000TO",
|
||||
swc1_2 = "e4000000HO",
|
||||
sdc1_2 = "f4000000HO",
|
||||
|
||||
-- Opcode SPECIAL.
|
||||
nop_0 = "00000000",
|
||||
sll_3 = "00000000DTA",
|
||||
movf_2 = "00000001DS",
|
||||
movf_3 = "00000001DSC",
|
||||
movt_2 = "00010001DS",
|
||||
movt_3 = "00010001DSC",
|
||||
srl_3 = "00000002DTA",
|
||||
rotr_3 = "00200002DTA",
|
||||
sra_3 = "00000003DTA",
|
||||
sllv_3 = "00000004DTS",
|
||||
srlv_3 = "00000006DTS",
|
||||
rotrv_3 = "00000046DTS",
|
||||
srav_3 = "00000007DTS",
|
||||
jr_1 = "00000008S",
|
||||
jalr_1 = "0000f809S",
|
||||
jalr_2 = "00000009DS",
|
||||
movz_3 = "0000000aDST",
|
||||
movn_3 = "0000000bDST",
|
||||
syscall_0 = "0000000c",
|
||||
syscall_1 = "0000000cY",
|
||||
break_0 = "0000000d",
|
||||
break_1 = "0000000dY",
|
||||
sync_0 = "0000000f",
|
||||
mfhi_1 = "00000010D",
|
||||
mthi_1 = "00000011S",
|
||||
mflo_1 = "00000012D",
|
||||
mtlo_1 = "00000013S",
|
||||
mult_2 = "00000018ST",
|
||||
multu_2 = "00000019ST",
|
||||
div_2 = "0000001aST",
|
||||
divu_2 = "0000001bST",
|
||||
add_3 = "00000020DST",
|
||||
move_2 = "00000021DS",
|
||||
addu_3 = "00000021DST",
|
||||
sub_3 = "00000022DST",
|
||||
negu_2 = "00000023DT",
|
||||
subu_3 = "00000023DST",
|
||||
and_3 = "00000024DST",
|
||||
or_3 = "00000025DST",
|
||||
xor_3 = "00000026DST",
|
||||
not_2 = "00000027DS",
|
||||
nor_3 = "00000027DST",
|
||||
slt_3 = "0000002aDST",
|
||||
sltu_3 = "0000002bDST",
|
||||
tge_2 = "00000030ST",
|
||||
tge_3 = "00000030STZ",
|
||||
tgeu_2 = "00000031ST",
|
||||
tgeu_3 = "00000031STZ",
|
||||
tlt_2 = "00000032ST",
|
||||
tlt_3 = "00000032STZ",
|
||||
tltu_2 = "00000033ST",
|
||||
tltu_3 = "00000033STZ",
|
||||
teq_2 = "00000034ST",
|
||||
teq_3 = "00000034STZ",
|
||||
tne_2 = "00000036ST",
|
||||
tne_3 = "00000036STZ",
|
||||
|
||||
-- Opcode REGIMM.
|
||||
bltz_2 = "04000000SB",
|
||||
bgez_2 = "04010000SB",
|
||||
bltzl_2 = "04020000SB",
|
||||
bgezl_2 = "04030000SB",
|
||||
tgei_2 = "04080000SI",
|
||||
tgeiu_2 = "04090000SI",
|
||||
tlti_2 = "040a0000SI",
|
||||
tltiu_2 = "040b0000SI",
|
||||
teqi_2 = "040c0000SI",
|
||||
tnei_2 = "040e0000SI",
|
||||
bltzal_2 = "04100000SB",
|
||||
bal_1 = "04110000B",
|
||||
bgezal_2 = "04110000SB",
|
||||
bltzall_2 = "04120000SB",
|
||||
bgezall_2 = "04130000SB",
|
||||
synci_1 = "041f0000O",
|
||||
|
||||
-- Opcode SPECIAL2.
|
||||
madd_2 = "70000000ST",
|
||||
maddu_2 = "70000001ST",
|
||||
mul_3 = "70000002DST",
|
||||
msub_2 = "70000004ST",
|
||||
msubu_2 = "70000005ST",
|
||||
clz_2 = "70000020DS=",
|
||||
clo_2 = "70000021DS=",
|
||||
sdbbp_0 = "7000003f",
|
||||
sdbbp_1 = "7000003fY",
|
||||
|
||||
-- Opcode SPECIAL3.
|
||||
ext_4 = "7c000000TSAM", -- Note: last arg is msbd = size-1
|
||||
ins_4 = "7c000004TSAM", -- Note: last arg is msb = pos+size-1
|
||||
wsbh_2 = "7c0000a0DT",
|
||||
seb_2 = "7c000420DT",
|
||||
seh_2 = "7c000620DT",
|
||||
rdhwr_2 = "7c00003bTD",
|
||||
|
||||
-- Opcode COP0.
|
||||
mfc0_2 = "40000000TD",
|
||||
mfc0_3 = "40000000TDW",
|
||||
mtc0_2 = "40800000TD",
|
||||
mtc0_3 = "40800000TDW",
|
||||
rdpgpr_2 = "41400000DT",
|
||||
di_0 = "41606000",
|
||||
di_1 = "41606000T",
|
||||
ei_0 = "41606020",
|
||||
ei_1 = "41606020T",
|
||||
wrpgpr_2 = "41c00000DT",
|
||||
tlbr_0 = "42000001",
|
||||
tlbwi_0 = "42000002",
|
||||
tlbwr_0 = "42000006",
|
||||
tlbp_0 = "42000008",
|
||||
eret_0 = "42000018",
|
||||
deret_0 = "4200001f",
|
||||
wait_0 = "42000020",
|
||||
|
||||
-- Opcode COP1.
|
||||
mfc1_2 = "44000000TG",
|
||||
cfc1_2 = "44400000TG",
|
||||
mfhc1_2 = "44600000TG",
|
||||
mtc1_2 = "44800000TG",
|
||||
ctc1_2 = "44c00000TG",
|
||||
mthc1_2 = "44e00000TG",
|
||||
|
||||
bc1f_1 = "45000000B",
|
||||
bc1f_2 = "45000000CB",
|
||||
bc1t_1 = "45010000B",
|
||||
bc1t_2 = "45010000CB",
|
||||
bc1fl_1 = "45020000B",
|
||||
bc1fl_2 = "45020000CB",
|
||||
bc1tl_1 = "45030000B",
|
||||
bc1tl_2 = "45030000CB",
|
||||
|
||||
["add.s_3"] = "46000000FGH",
|
||||
["sub.s_3"] = "46000001FGH",
|
||||
["mul.s_3"] = "46000002FGH",
|
||||
["div.s_3"] = "46000003FGH",
|
||||
["sqrt.s_2"] = "46000004FG",
|
||||
["abs.s_2"] = "46000005FG",
|
||||
["mov.s_2"] = "46000006FG",
|
||||
["neg.s_2"] = "46000007FG",
|
||||
["round.l.s_2"] = "46000008FG",
|
||||
["trunc.l.s_2"] = "46000009FG",
|
||||
["ceil.l.s_2"] = "4600000aFG",
|
||||
["floor.l.s_2"] = "4600000bFG",
|
||||
["round.w.s_2"] = "4600000cFG",
|
||||
["trunc.w.s_2"] = "4600000dFG",
|
||||
["ceil.w.s_2"] = "4600000eFG",
|
||||
["floor.w.s_2"] = "4600000fFG",
|
||||
["movf.s_2"] = "46000011FG",
|
||||
["movf.s_3"] = "46000011FGC",
|
||||
["movt.s_2"] = "46010011FG",
|
||||
["movt.s_3"] = "46010011FGC",
|
||||
["movz.s_3"] = "46000012FGT",
|
||||
["movn.s_3"] = "46000013FGT",
|
||||
["recip.s_2"] = "46000015FG",
|
||||
["rsqrt.s_2"] = "46000016FG",
|
||||
["cvt.d.s_2"] = "46000021FG",
|
||||
["cvt.w.s_2"] = "46000024FG",
|
||||
["cvt.l.s_2"] = "46000025FG",
|
||||
["cvt.ps.s_3"] = "46000026FGH",
|
||||
["c.f.s_2"] = "46000030GH",
|
||||
["c.f.s_3"] = "46000030VGH",
|
||||
["c.un.s_2"] = "46000031GH",
|
||||
["c.un.s_3"] = "46000031VGH",
|
||||
["c.eq.s_2"] = "46000032GH",
|
||||
["c.eq.s_3"] = "46000032VGH",
|
||||
["c.ueq.s_2"] = "46000033GH",
|
||||
["c.ueq.s_3"] = "46000033VGH",
|
||||
["c.olt.s_2"] = "46000034GH",
|
||||
["c.olt.s_3"] = "46000034VGH",
|
||||
["c.ult.s_2"] = "46000035GH",
|
||||
["c.ult.s_3"] = "46000035VGH",
|
||||
["c.ole.s_2"] = "46000036GH",
|
||||
["c.ole.s_3"] = "46000036VGH",
|
||||
["c.ule.s_2"] = "46000037GH",
|
||||
["c.ule.s_3"] = "46000037VGH",
|
||||
["c.sf.s_2"] = "46000038GH",
|
||||
["c.sf.s_3"] = "46000038VGH",
|
||||
["c.ngle.s_2"] = "46000039GH",
|
||||
["c.ngle.s_3"] = "46000039VGH",
|
||||
["c.seq.s_2"] = "4600003aGH",
|
||||
["c.seq.s_3"] = "4600003aVGH",
|
||||
["c.ngl.s_2"] = "4600003bGH",
|
||||
["c.ngl.s_3"] = "4600003bVGH",
|
||||
["c.lt.s_2"] = "4600003cGH",
|
||||
["c.lt.s_3"] = "4600003cVGH",
|
||||
["c.nge.s_2"] = "4600003dGH",
|
||||
["c.nge.s_3"] = "4600003dVGH",
|
||||
["c.le.s_2"] = "4600003eGH",
|
||||
["c.le.s_3"] = "4600003eVGH",
|
||||
["c.ngt.s_2"] = "4600003fGH",
|
||||
["c.ngt.s_3"] = "4600003fVGH",
|
||||
|
||||
["add.d_3"] = "46200000FGH",
|
||||
["sub.d_3"] = "46200001FGH",
|
||||
["mul.d_3"] = "46200002FGH",
|
||||
["div.d_3"] = "46200003FGH",
|
||||
["sqrt.d_2"] = "46200004FG",
|
||||
["abs.d_2"] = "46200005FG",
|
||||
["mov.d_2"] = "46200006FG",
|
||||
["neg.d_2"] = "46200007FG",
|
||||
["round.l.d_2"] = "46200008FG",
|
||||
["trunc.l.d_2"] = "46200009FG",
|
||||
["ceil.l.d_2"] = "4620000aFG",
|
||||
["floor.l.d_2"] = "4620000bFG",
|
||||
["round.w.d_2"] = "4620000cFG",
|
||||
["trunc.w.d_2"] = "4620000dFG",
|
||||
["ceil.w.d_2"] = "4620000eFG",
|
||||
["floor.w.d_2"] = "4620000fFG",
|
||||
["movf.d_2"] = "46200011FG",
|
||||
["movf.d_3"] = "46200011FGC",
|
||||
["movt.d_2"] = "46210011FG",
|
||||
["movt.d_3"] = "46210011FGC",
|
||||
["movz.d_3"] = "46200012FGT",
|
||||
["movn.d_3"] = "46200013FGT",
|
||||
["recip.d_2"] = "46200015FG",
|
||||
["rsqrt.d_2"] = "46200016FG",
|
||||
["cvt.s.d_2"] = "46200020FG",
|
||||
["cvt.w.d_2"] = "46200024FG",
|
||||
["cvt.l.d_2"] = "46200025FG",
|
||||
["c.f.d_2"] = "46200030GH",
|
||||
["c.f.d_3"] = "46200030VGH",
|
||||
["c.un.d_2"] = "46200031GH",
|
||||
["c.un.d_3"] = "46200031VGH",
|
||||
["c.eq.d_2"] = "46200032GH",
|
||||
["c.eq.d_3"] = "46200032VGH",
|
||||
["c.ueq.d_2"] = "46200033GH",
|
||||
["c.ueq.d_3"] = "46200033VGH",
|
||||
["c.olt.d_2"] = "46200034GH",
|
||||
["c.olt.d_3"] = "46200034VGH",
|
||||
["c.ult.d_2"] = "46200035GH",
|
||||
["c.ult.d_3"] = "46200035VGH",
|
||||
["c.ole.d_2"] = "46200036GH",
|
||||
["c.ole.d_3"] = "46200036VGH",
|
||||
["c.ule.d_2"] = "46200037GH",
|
||||
["c.ule.d_3"] = "46200037VGH",
|
||||
["c.sf.d_2"] = "46200038GH",
|
||||
["c.sf.d_3"] = "46200038VGH",
|
||||
["c.ngle.d_2"] = "46200039GH",
|
||||
["c.ngle.d_3"] = "46200039VGH",
|
||||
["c.seq.d_2"] = "4620003aGH",
|
||||
["c.seq.d_3"] = "4620003aVGH",
|
||||
["c.ngl.d_2"] = "4620003bGH",
|
||||
["c.ngl.d_3"] = "4620003bVGH",
|
||||
["c.lt.d_2"] = "4620003cGH",
|
||||
["c.lt.d_3"] = "4620003cVGH",
|
||||
["c.nge.d_2"] = "4620003dGH",
|
||||
["c.nge.d_3"] = "4620003dVGH",
|
||||
["c.le.d_2"] = "4620003eGH",
|
||||
["c.le.d_3"] = "4620003eVGH",
|
||||
["c.ngt.d_2"] = "4620003fGH",
|
||||
["c.ngt.d_3"] = "4620003fVGH",
|
||||
|
||||
["add.ps_3"] = "46c00000FGH",
|
||||
["sub.ps_3"] = "46c00001FGH",
|
||||
["mul.ps_3"] = "46c00002FGH",
|
||||
["abs.ps_2"] = "46c00005FG",
|
||||
["mov.ps_2"] = "46c00006FG",
|
||||
["neg.ps_2"] = "46c00007FG",
|
||||
["movf.ps_2"] = "46c00011FG",
|
||||
["movf.ps_3"] = "46c00011FGC",
|
||||
["movt.ps_2"] = "46c10011FG",
|
||||
["movt.ps_3"] = "46c10011FGC",
|
||||
["movz.ps_3"] = "46c00012FGT",
|
||||
["movn.ps_3"] = "46c00013FGT",
|
||||
["cvt.s.pu_2"] = "46c00020FG",
|
||||
["cvt.s.pl_2"] = "46c00028FG",
|
||||
["pll.ps_3"] = "46c0002cFGH",
|
||||
["plu.ps_3"] = "46c0002dFGH",
|
||||
["pul.ps_3"] = "46c0002eFGH",
|
||||
["puu.ps_3"] = "46c0002fFGH",
|
||||
["c.f.ps_2"] = "46c00030GH",
|
||||
["c.f.ps_3"] = "46c00030VGH",
|
||||
["c.un.ps_2"] = "46c00031GH",
|
||||
["c.un.ps_3"] = "46c00031VGH",
|
||||
["c.eq.ps_2"] = "46c00032GH",
|
||||
["c.eq.ps_3"] = "46c00032VGH",
|
||||
["c.ueq.ps_2"] = "46c00033GH",
|
||||
["c.ueq.ps_3"] = "46c00033VGH",
|
||||
["c.olt.ps_2"] = "46c00034GH",
|
||||
["c.olt.ps_3"] = "46c00034VGH",
|
||||
["c.ult.ps_2"] = "46c00035GH",
|
||||
["c.ult.ps_3"] = "46c00035VGH",
|
||||
["c.ole.ps_2"] = "46c00036GH",
|
||||
["c.ole.ps_3"] = "46c00036VGH",
|
||||
["c.ule.ps_2"] = "46c00037GH",
|
||||
["c.ule.ps_3"] = "46c00037VGH",
|
||||
["c.sf.ps_2"] = "46c00038GH",
|
||||
["c.sf.ps_3"] = "46c00038VGH",
|
||||
["c.ngle.ps_2"] = "46c00039GH",
|
||||
["c.ngle.ps_3"] = "46c00039VGH",
|
||||
["c.seq.ps_2"] = "46c0003aGH",
|
||||
["c.seq.ps_3"] = "46c0003aVGH",
|
||||
["c.ngl.ps_2"] = "46c0003bGH",
|
||||
["c.ngl.ps_3"] = "46c0003bVGH",
|
||||
["c.lt.ps_2"] = "46c0003cGH",
|
||||
["c.lt.ps_3"] = "46c0003cVGH",
|
||||
["c.nge.ps_2"] = "46c0003dGH",
|
||||
["c.nge.ps_3"] = "46c0003dVGH",
|
||||
["c.le.ps_2"] = "46c0003eGH",
|
||||
["c.le.ps_3"] = "46c0003eVGH",
|
||||
["c.ngt.ps_2"] = "46c0003fGH",
|
||||
["c.ngt.ps_3"] = "46c0003fVGH",
|
||||
|
||||
["cvt.s.w_2"] = "46800020FG",
|
||||
["cvt.d.w_2"] = "46800021FG",
|
||||
|
||||
["cvt.s.l_2"] = "46a00020FG",
|
||||
["cvt.d.l_2"] = "46a00021FG",
|
||||
|
||||
-- Opcode COP1X.
|
||||
lwxc1_2 = "4c000000FX",
|
||||
ldxc1_2 = "4c000001FX",
|
||||
luxc1_2 = "4c000005FX",
|
||||
swxc1_2 = "4c000008FX",
|
||||
sdxc1_2 = "4c000009FX",
|
||||
suxc1_2 = "4c00000dFX",
|
||||
prefx_2 = "4c00000fMX",
|
||||
["alnv.ps_4"] = "4c00001eFGHS",
|
||||
["madd.s_4"] = "4c000020FRGH",
|
||||
["madd.d_4"] = "4c000021FRGH",
|
||||
["madd.ps_4"] = "4c000026FRGH",
|
||||
["msub.s_4"] = "4c000028FRGH",
|
||||
["msub.d_4"] = "4c000029FRGH",
|
||||
["msub.ps_4"] = "4c00002eFRGH",
|
||||
["nmadd.s_4"] = "4c000030FRGH",
|
||||
["nmadd.d_4"] = "4c000031FRGH",
|
||||
["nmadd.ps_4"] = "4c000036FRGH",
|
||||
["nmsub.s_4"] = "4c000038FRGH",
|
||||
["nmsub.d_4"] = "4c000039FRGH",
|
||||
["nmsub.ps_4"] = "4c00003eFRGH",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function parse_gpr(expr)
|
||||
local tname, ovreg = match(expr, "^([%w_]+):(r[1-3]?[0-9])$")
|
||||
local tp = map_type[tname or expr]
|
||||
if tp then
|
||||
local reg = ovreg or tp.reg
|
||||
if not reg then
|
||||
werror("type `"..(tname or expr).."' needs a register override")
|
||||
end
|
||||
expr = reg
|
||||
end
|
||||
local r = match(expr, "^r([1-3]?[0-9])$")
|
||||
if r then
|
||||
r = tonumber(r)
|
||||
if r <= 31 then return r, tp end
|
||||
end
|
||||
werror("bad register name `"..expr.."'")
|
||||
end
|
||||
|
||||
local function parse_fpr(expr)
|
||||
local r = match(expr, "^f([1-3]?[0-9])$")
|
||||
if r then
|
||||
r = tonumber(r)
|
||||
if r <= 31 then return r end
|
||||
end
|
||||
werror("bad register name `"..expr.."'")
|
||||
end
|
||||
|
||||
local function parse_imm(imm, bits, shift, scale, signed)
|
||||
local n = tonumber(imm)
|
||||
if n then
|
||||
local m = sar(n, scale)
|
||||
if shl(m, scale) == n then
|
||||
if signed then
|
||||
local s = sar(m, bits-1)
|
||||
if s == 0 then return shl(m, shift)
|
||||
elseif s == -1 then return shl(m + shl(1, bits), shift) end
|
||||
else
|
||||
if sar(m, bits) == 0 then return shl(m, shift) end
|
||||
end
|
||||
end
|
||||
werror("out of range immediate `"..imm.."'")
|
||||
elseif match(imm, "^[rf]([1-3]?[0-9])$") or
|
||||
match(imm, "^([%w_]+):([rf][1-3]?[0-9])$") then
|
||||
werror("expected immediate operand, got register")
|
||||
else
|
||||
waction("IMM", (signed and 32768 or 0)+scale*1024+bits*32+shift, imm)
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_disp(disp)
|
||||
local imm, reg = match(disp, "^(.*)%(([%w_:]+)%)$")
|
||||
if imm then
|
||||
local r = shl(parse_gpr(reg), 21)
|
||||
local extname = match(imm, "^extern%s+(%S+)$")
|
||||
if extname then
|
||||
waction("REL_EXT", map_extern[extname], nil, 1)
|
||||
return r
|
||||
else
|
||||
return r + parse_imm(imm, 16, 0, 0, true)
|
||||
end
|
||||
end
|
||||
local reg, tailr = match(disp, "^([%w_:]+)%s*(.*)$")
|
||||
if reg and tailr ~= "" then
|
||||
local r, tp = parse_gpr(reg)
|
||||
if tp then
|
||||
waction("IMM", 32768+16*32, format(tp.ctypefmt, tailr))
|
||||
return shl(r, 21)
|
||||
end
|
||||
end
|
||||
werror("bad displacement `"..disp.."'")
|
||||
end
|
||||
|
||||
local function parse_index(idx)
|
||||
local rt, rs = match(idx, "^(.*)%(([%w_:]+)%)$")
|
||||
if rt then
|
||||
rt = parse_gpr(rt)
|
||||
rs = parse_gpr(rs)
|
||||
return shl(rt, 16) + shl(rs, 21)
|
||||
end
|
||||
werror("bad index `"..idx.."'")
|
||||
end
|
||||
|
||||
local function parse_label(label, def)
|
||||
local prefix = sub(label, 1, 2)
|
||||
-- =>label (pc label reference)
|
||||
if prefix == "=>" then
|
||||
return "PC", 0, sub(label, 3)
|
||||
end
|
||||
-- ->name (global label reference)
|
||||
if prefix == "->" then
|
||||
return "LG", map_global[sub(label, 3)]
|
||||
end
|
||||
if def then
|
||||
-- [1-9] (local label definition)
|
||||
if match(label, "^[1-9]$") then
|
||||
return "LG", 10+tonumber(label)
|
||||
end
|
||||
else
|
||||
-- [<>][1-9] (local label reference)
|
||||
local dir, lnum = match(label, "^([<>])([1-9])$")
|
||||
if dir then -- Fwd: 1-9, Bkwd: 11-19.
|
||||
return "LG", lnum + (dir == ">" and 0 or 10)
|
||||
end
|
||||
-- extern label (extern label reference)
|
||||
local extname = match(label, "^extern%s+(%S+)$")
|
||||
if extname then
|
||||
return "EXT", map_extern[extname]
|
||||
end
|
||||
end
|
||||
werror("bad label `"..label.."'")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Handle opcodes defined with template strings.
|
||||
map_op[".template__"] = function(params, template, nparams)
|
||||
if not params then return sub(template, 9) end
|
||||
local op = tonumber(sub(template, 1, 8), 16)
|
||||
local n = 1
|
||||
|
||||
-- Limit number of section buffer positions used by a single dasm_put().
|
||||
-- A single opcode needs a maximum of 2 positions (ins/ext).
|
||||
if secpos+2 > maxsecpos then wflush() end
|
||||
local pos = wpos()
|
||||
|
||||
-- Process each character.
|
||||
for p in gmatch(sub(template, 9), ".") do
|
||||
if p == "D" then
|
||||
op = op + shl(parse_gpr(params[n]), 11); n = n + 1
|
||||
elseif p == "T" then
|
||||
op = op + shl(parse_gpr(params[n]), 16); n = n + 1
|
||||
elseif p == "S" then
|
||||
op = op + shl(parse_gpr(params[n]), 21); n = n + 1
|
||||
elseif p == "F" then
|
||||
op = op + shl(parse_fpr(params[n]), 6); n = n + 1
|
||||
elseif p == "G" then
|
||||
op = op + shl(parse_fpr(params[n]), 11); n = n + 1
|
||||
elseif p == "H" then
|
||||
op = op + shl(parse_fpr(params[n]), 16); n = n + 1
|
||||
elseif p == "R" then
|
||||
op = op + shl(parse_fpr(params[n]), 21); n = n + 1
|
||||
elseif p == "I" then
|
||||
op = op + parse_imm(params[n], 16, 0, 0, true); n = n + 1
|
||||
elseif p == "U" then
|
||||
op = op + parse_imm(params[n], 16, 0, 0, false); n = n + 1
|
||||
elseif p == "O" then
|
||||
op = op + parse_disp(params[n]); n = n + 1
|
||||
elseif p == "X" then
|
||||
op = op + parse_index(params[n]); n = n + 1
|
||||
elseif p == "B" or p == "J" then
|
||||
local mode, n, s = parse_label(params[n], false)
|
||||
if p == "B" then n = n + 2048 end
|
||||
waction("REL_"..mode, n, s, 1)
|
||||
n = n + 1
|
||||
elseif p == "A" then
|
||||
op = op + parse_imm(params[n], 5, 6, 0, false); n = n + 1
|
||||
elseif p == "M" then
|
||||
op = op + parse_imm(params[n], 5, 11, 0, false); n = n + 1
|
||||
elseif p == "N" then
|
||||
op = op + parse_imm(params[n], 5, 16, 0, false); n = n + 1
|
||||
elseif p == "C" then
|
||||
op = op + parse_imm(params[n], 3, 18, 0, false); n = n + 1
|
||||
elseif p == "V" then
|
||||
op = op + parse_imm(params[n], 3, 8, 0, false); n = n + 1
|
||||
elseif p == "W" then
|
||||
op = op + parse_imm(params[n], 3, 0, 0, false); n = n + 1
|
||||
elseif p == "Y" then
|
||||
op = op + parse_imm(params[n], 20, 6, 0, false); n = n + 1
|
||||
elseif p == "Z" then
|
||||
op = op + parse_imm(params[n], 10, 6, 0, false); n = n + 1
|
||||
elseif p == "=" then
|
||||
op = op + shl(band(op, 0xf800), 5) -- Copy D to T for clz, clo.
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
wputpos(pos, op)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcode to mark the position where the action list is to be emitted.
|
||||
map_op[".actionlist_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeactions(out, name) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the global enum is to be emitted.
|
||||
map_op[".globals_1"] = function(params)
|
||||
if not params then return "prefix" end
|
||||
local prefix = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeglobals(out, prefix) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the global names are to be emitted.
|
||||
map_op[".globalnames_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeglobalnames(out, name) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the extern names are to be emitted.
|
||||
map_op[".externnames_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeexternnames(out, name) end)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Label pseudo-opcode (converted from trailing colon form).
|
||||
map_op[".label_1"] = function(params)
|
||||
if not params then return "[1-9] | ->global | =>pcexpr" end
|
||||
if secpos+1 > maxsecpos then wflush() end
|
||||
local mode, n, s = parse_label(params[1], true)
|
||||
if mode == "EXT" then werror("bad label definition") end
|
||||
waction("LABEL_"..mode, n, s, 1)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcodes for data storage.
|
||||
map_op[".long_*"] = function(params)
|
||||
if not params then return "imm..." end
|
||||
for _,p in ipairs(params) do
|
||||
local n = tonumber(p)
|
||||
if not n then werror("bad immediate `"..p.."'") end
|
||||
if n < 0 then n = n + 2^32 end
|
||||
wputw(n)
|
||||
if secpos+2 > maxsecpos then wflush() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Alignment pseudo-opcode.
|
||||
map_op[".align_1"] = function(params)
|
||||
if not params then return "numpow2" end
|
||||
if secpos+1 > maxsecpos then wflush() end
|
||||
local align = tonumber(params[1])
|
||||
if align then
|
||||
local x = align
|
||||
-- Must be a power of 2 in the range (2 ... 256).
|
||||
for i=1,8 do
|
||||
x = x / 2
|
||||
if x == 1 then
|
||||
waction("ALIGN", align-1, nil, 1) -- Action byte is 2**n-1.
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
werror("bad alignment")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcode for (primitive) type definitions (map to C types).
|
||||
map_op[".type_3"] = function(params, nparams)
|
||||
if not params then
|
||||
return nparams == 2 and "name, ctype" or "name, ctype, reg"
|
||||
end
|
||||
local name, ctype, reg = params[1], params[2], params[3]
|
||||
if not match(name, "^[%a_][%w_]*$") then
|
||||
werror("bad type name `"..name.."'")
|
||||
end
|
||||
local tp = map_type[name]
|
||||
if tp then
|
||||
werror("duplicate type `"..name.."'")
|
||||
end
|
||||
-- Add #type to defines. A bit unclean to put it in map_archdef.
|
||||
map_archdef["#"..name] = "sizeof("..ctype..")"
|
||||
-- Add new type and emit shortcut define.
|
||||
local num = ctypenum + 1
|
||||
map_type[name] = {
|
||||
ctype = ctype,
|
||||
ctypefmt = format("Dt%X(%%s)", num),
|
||||
reg = reg,
|
||||
}
|
||||
wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
|
||||
ctypenum = num
|
||||
end
|
||||
map_op[".type_2"] = map_op[".type_3"]
|
||||
|
||||
-- Dump type definitions.
|
||||
local function dumptypes(out, lvl)
|
||||
local t = {}
|
||||
for name in pairs(map_type) do t[#t+1] = name end
|
||||
sort(t)
|
||||
out:write("Type definitions:\n")
|
||||
for _,name in ipairs(t) do
|
||||
local tp = map_type[name]
|
||||
local reg = tp.reg or ""
|
||||
out:write(format(" %-20s %-20s %s\n", name, tp.ctype, reg))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Set the current section.
|
||||
function _M.section(num)
|
||||
waction("SECTION", num)
|
||||
wflush(true) -- SECTION is a terminal action.
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump architecture description.
|
||||
function _M.dumparch(out)
|
||||
out:write(format("DynASM %s version %s, released %s\n\n",
|
||||
_info.arch, _info.version, _info.release))
|
||||
dumpactions(out)
|
||||
end
|
||||
|
||||
-- Dump all user defined elements.
|
||||
function _M.dumpdef(out, lvl)
|
||||
dumptypes(out, lvl)
|
||||
dumpglobals(out, lvl)
|
||||
dumpexterns(out, lvl)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pass callbacks from/to the DynASM core.
|
||||
function _M.passcb(wl, we, wf, ww)
|
||||
wline, werror, wfatal, wwarn = wl, we, wf, ww
|
||||
return wflush
|
||||
end
|
||||
|
||||
-- Setup the arch-specific module.
|
||||
function _M.setup(arch, opt)
|
||||
g_arch, g_opt = arch, opt
|
||||
end
|
||||
|
||||
-- Merge the core maps and the arch-specific maps.
|
||||
function _M.mergemaps(map_coreop, map_def)
|
||||
setmetatable(map_op, { __index = map_coreop })
|
||||
setmetatable(map_def, { __index = map_archdef })
|
||||
return map_op, map_def
|
||||
end
|
||||
|
||||
return _M
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,676 +0,0 @@
|
||||
##############################################################################
|
||||
# LuaJIT Makefile. Requires GNU Make.
|
||||
#
|
||||
# Please read doc/install.html before changing any variables!
|
||||
#
|
||||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
||||
# Also works with MinGW and Cygwin on Windows.
|
||||
# Please check msvcbuild.bat for building with MSVC on Windows.
|
||||
#
|
||||
# Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h
|
||||
##############################################################################
|
||||
|
||||
MAJVER= 2
|
||||
MINVER= 0
|
||||
RELVER= 0
|
||||
ABIVER= 5.1
|
||||
NODOTABIVER= 51
|
||||
|
||||
##############################################################################
|
||||
############################# COMPILER OPTIONS #############################
|
||||
##############################################################################
|
||||
# These options mainly affect the speed of the JIT compiler itself, not the
|
||||
# speed of the JIT-compiled code. Turn any of the optional settings on by
|
||||
# removing the '#' in front of them. Make sure you force a full recompile
|
||||
# with "make clean", followed by "make" if you change any options.
|
||||
#
|
||||
# LuaJIT builds as a native 32 or 64 bit binary by default.
|
||||
CC= gcc
|
||||
#
|
||||
# Use this if you want to force a 32 bit build on a 64 bit multilib OS.
|
||||
#CC= gcc -m32
|
||||
#
|
||||
# Since the assembler part does NOT maintain a frame pointer, it's pointless
|
||||
# to slow down the C part by not omitting it. Debugging, tracebacks and
|
||||
# unwinding are not affected -- the assembler part has frame unwind
|
||||
# information and GCC emits it where needed (x64) or with -g (see CCDEBUG).
|
||||
CCOPT= -O2 -fomit-frame-pointer
|
||||
# Use this if you want to generate a smaller binary (but it's slower):
|
||||
#CCOPT= -Os -fomit-frame-pointer
|
||||
# Note: it's no longer recommended to use -O3 with GCC 4.x.
|
||||
# The I-Cache bloat usually outweighs the benefits from aggressive inlining.
|
||||
#
|
||||
# Target-specific compiler options:
|
||||
#
|
||||
# x86 only: it's recommended to compile at least for i686. Better yet,
|
||||
# compile for an architecture that has SSE2, too (-msse -msse2).
|
||||
#
|
||||
# x86/x64 only: For GCC 4.2 or higher and if you don't intend to distribute
|
||||
# the binaries to a different machine you could also use: -march=native
|
||||
#
|
||||
CCOPT_x86= -march=i686
|
||||
CCOPT_x64=
|
||||
CCOPT_arm=
|
||||
CCOPT_ppc=
|
||||
CCOPT_ppcspe=
|
||||
CCOPT_mips=
|
||||
#
|
||||
CCDEBUG=
|
||||
# Uncomment the next line to generate debug information:
|
||||
#CCDEBUG= -g
|
||||
#
|
||||
CCWARN= -Wall
|
||||
# Uncomment the next line to enable more warnings:
|
||||
#CCWARN+= -Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
################################ BUILD MODE ################################
|
||||
##############################################################################
|
||||
# The default build mode is mixed mode on POSIX. On Windows this is the same
|
||||
# as dynamic mode.
|
||||
#
|
||||
# Mixed mode creates a static + dynamic library and a statically linked luajit.
|
||||
BUILDMODE= mixed
|
||||
#
|
||||
# Static mode creates a static library and a statically linked luajit.
|
||||
#BUILDMODE= static
|
||||
#
|
||||
# Dynamic mode creates a dynamic library and a dynamically linked luajit.
|
||||
# Note: this executable will only run when the library is installed!
|
||||
#BUILDMODE= dynamic
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
################################# FEATURES #################################
|
||||
##############################################################################
|
||||
# Enable/disable these features as needed, but make sure you force a full
|
||||
# recompile with "make clean", followed by "make".
|
||||
XCFLAGS=
|
||||
#
|
||||
# Permanently disable the FFI extension to reduce the size of the LuaJIT
|
||||
# executable. But please consider that the FFI library is compiled-in,
|
||||
# but NOT loaded by default. It only allocates any memory, if you actually
|
||||
# make use of it.
|
||||
#XCFLAGS+= -DLUAJIT_DISABLE_FFI
|
||||
#
|
||||
# Features from Lua 5.2 that are unlikely to break existing code are
|
||||
# enabled by default. Some other features that *might* break some existing
|
||||
# code (e.g. __pairs or os.execute() return values) can be enabled here.
|
||||
# Note: this does not provide full compatibility with Lua 5.2 at this time.
|
||||
#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT
|
||||
#
|
||||
# Disable the JIT compiler, i.e. turn LuaJIT into a pure interpreter.
|
||||
#XCFLAGS+= -DLUAJIT_DISABLE_JIT
|
||||
#
|
||||
# Some architectures (e.g. PPC) can use either single-number (1) or
|
||||
# dual-number (2) mode. Uncomment one of these lines to override the
|
||||
# default mode. Please see LJ_ARCH_NUMMODE in lj_arch.h for details.
|
||||
#XCFLAGS+= -DLUAJIT_NUMMODE=1
|
||||
#XCFLAGS+= -DLUAJIT_NUMMODE=2
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
############################ DEBUGGING SUPPORT #############################
|
||||
##############################################################################
|
||||
# Enable these options as needed, but make sure you force a full recompile
|
||||
# with "make clean", followed by "make".
|
||||
# Note that most of these are NOT suitable for benchmarking or release mode!
|
||||
#
|
||||
# Use the system provided memory allocator (realloc) instead of the
|
||||
# bundled memory allocator. This is slower, but sometimes helpful for
|
||||
# debugging. It's helpful for Valgrind's memcheck tool, too. This option
|
||||
# cannot be enabled on x64, since the built-in allocator is mandatory.
|
||||
#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
||||
#
|
||||
# This define is required to run LuaJIT under Valgrind. The Valgrind
|
||||
# header files must be installed. You should enable debug information, too.
|
||||
# Use --suppressions=lj.supp to avoid some false positives.
|
||||
#XCFLAGS+= -DLUAJIT_USE_VALGRIND
|
||||
#
|
||||
# This is the client for the GDB JIT API. GDB 7.0 or higher is required
|
||||
# to make use of it. See lj_gdbjit.c for details. Enabling this causes
|
||||
# a non-negligible overhead, even when not running under GDB.
|
||||
#XCFLAGS+= -DLUAJIT_USE_GDBJIT
|
||||
#
|
||||
# Turn on assertions for the Lua/C API to debug problems with lua_* calls.
|
||||
# This is rather slow -- use only while developing C libraries/embeddings.
|
||||
#XCFLAGS+= -DLUA_USE_APICHECK
|
||||
#
|
||||
# Turn on assertions for the whole LuaJIT VM. This significantly slows down
|
||||
# everything. Use only if you suspect a problem with LuaJIT itself.
|
||||
#XCFLAGS+= -DLUA_USE_ASSERT
|
||||
#
|
||||
##############################################################################
|
||||
# You probably don't need to change anything below this line!
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Flags and options for host and target.
|
||||
##############################################################################
|
||||
|
||||
# You can override the following variables at the make command line:
|
||||
# CC HOST_CC STATIC_CC DYNAMIC_CC
|
||||
# CFLAGS HOST_CFLAGS TARGET_CFLAGS
|
||||
# LDFLAGS HOST_LDFLAGS TARGET_LDFLAGS TARGET_SHLDFLAGS
|
||||
# LIBS HOST_LIBS TARGET_LIBS
|
||||
# CROSS HOST_SYS TARGET_SYS TARGET_FLAGS
|
||||
#
|
||||
# Cross-compilation examples:
|
||||
# make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows
|
||||
# make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu-
|
||||
|
||||
CCOPTIONS= $(CCDEBUG) $(CCOPT) $(CCWARN) $(XCFLAGS) $(CFLAGS)
|
||||
LDOPTIONS= $(CCDEBUG) $(LDFLAGS)
|
||||
|
||||
HOST_CC= $(CC)
|
||||
HOST_RM= rm -f
|
||||
# If left blank, minilua is built and used. You can supply an installed
|
||||
# copy of (plain) Lua 5.1 or 5.2, plus Lua BitOp. E.g. with: HOST_LUA=lua
|
||||
HOST_LUA=
|
||||
|
||||
HOST_XCFLAGS= -I.
|
||||
HOST_XLDFLAGS=
|
||||
HOST_XLIBS=
|
||||
HOST_ACFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH) $(HOST_CFLAGS)
|
||||
HOST_ALDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS) $(HOST_LDFLAGS)
|
||||
HOST_ALIBS= $(HOST_XLIBS) $(LIBS) $(HOST_LIBS)
|
||||
|
||||
STATIC_CC = $(CROSS)$(CC)
|
||||
DYNAMIC_CC = $(CROSS)$(CC) -fPIC
|
||||
TARGET_CC= $(STATIC_CC)
|
||||
TARGET_STCC= $(STATIC_CC)
|
||||
TARGET_DYNCC= $(DYNAMIC_CC)
|
||||
TARGET_LD= $(CROSS)$(CC)
|
||||
TARGET_AR= $(CROSS)ar rcus
|
||||
TARGET_STRIP= $(CROSS)strip
|
||||
|
||||
TARGET_SONAME= libluajit.so
|
||||
TARGET_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).dylib
|
||||
TARGET_DYLIBPATH= $(or $(PREFIX),/usr/local)/lib/$(TARGET_DYLIBNAME)
|
||||
TARGET_DLLNAME= lua$(NODOTABIVER).dll
|
||||
TARGET_XSHLDFLAGS= -shared -fPIC -Wl,-soname,$(TARGET_SONAME)
|
||||
TARGET_DYNXLDOPTS=
|
||||
|
||||
TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE
|
||||
TARGET_XLDFLAGS=
|
||||
TARGET_XLIBS= -lm
|
||||
TARGET_TCFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS)
|
||||
TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS)
|
||||
TARGET_ALDFLAGS= $(LDOPTIONS) $(TARGET_XLDFLAGS) $(TARGET_FLAGS) $(TARGET_LDFLAGS)
|
||||
TARGET_ASHLDFLAGS= $(LDOPTIONS) $(TARGET_XSHLDFLAGS) $(TARGET_FLAGS) $(TARGET_SHLDFLAGS)
|
||||
TARGET_ALIBS= $(TARGET_XLIBS) $(LIBS) $(TARGET_LIBS)
|
||||
|
||||
TARGET_TESTARCH=$(shell $(TARGET_CC) $(TARGET_TCFLAGS) -E lj_arch.h -dM)
|
||||
ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x64
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x86
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= arm
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= ppc
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_PPCSPE ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= ppcspe
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -D__MIPSEL__=1
|
||||
endif
|
||||
TARGET_LJARCH= mips
|
||||
else
|
||||
$(error Unsupported target architecture)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH)))
|
||||
TARGET_SYS= PS3
|
||||
TARGET_ARCH+= -D__CELLOS_LV2__
|
||||
TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
||||
endif
|
||||
ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH+= -DLUAJIT_NO_UNWIND
|
||||
endif
|
||||
|
||||
TARGET_XCFLAGS+= $(CCOPT_$(TARGET_LJARCH))
|
||||
TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH))
|
||||
|
||||
ifneq (,$(PREFIX))
|
||||
ifneq (/usr/local,$(PREFIX))
|
||||
TARGET_XCFLAGS+= -DLUA_XROOT=\"$(PREFIX)/\"
|
||||
ifneq (/usr,$(PREFIX))
|
||||
TARGET_DYNXLDOPTS= -Wl,-rpath,$(PREFIX)/lib
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# System detection.
|
||||
##############################################################################
|
||||
|
||||
ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM))
|
||||
HOST_SYS= Windows
|
||||
HOST_RM= del
|
||||
else
|
||||
HOST_SYS:= $(shell uname -s)
|
||||
ifneq (,$(findstring MINGW,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
ifneq (,$(findstring CYGWIN,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= cygwin
|
||||
endif
|
||||
endif
|
||||
|
||||
TARGET_SYS?= $(HOST_SYS)
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
TARGET_STRIP+= --strip-unneeded
|
||||
TARGET_XSHLDFLAGS= -shared
|
||||
TARGET_DYNXLDOPTS=
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
ifeq (,$(MACOSX_DEPLOYMENT_TARGET))
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.4
|
||||
endif
|
||||
TARGET_STRIP+= -x
|
||||
TARGET_AR+= 2>/dev/null
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC
|
||||
TARGET_DYNXLDOPTS=
|
||||
TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER)
|
||||
ifeq (x64,$(TARGET_LJARCH))
|
||||
TARGET_XLDFLAGS+= -pagezero_size 10000 -image_base 100000000
|
||||
TARGET_XSHLDFLAGS+= -image_base 7fff04c4a000
|
||||
endif
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
TARGET_STRIP+= -x
|
||||
TARGET_AR+= 2>/dev/null
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC
|
||||
TARGET_DYNXLDOPTS=
|
||||
TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER)
|
||||
else
|
||||
ifneq (,$(findstring stack-protector,$(shell $(TARGET_CC) -dumpspecs)))
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
endif
|
||||
ifneq (SunOS,$(TARGET_SYS))
|
||||
ifneq (PS3,$(TARGET_SYS))
|
||||
TARGET_XLDFLAGS+= -Wl,-E
|
||||
endif
|
||||
endif
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
ifeq (GNU/kFreeBSD,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(HOST_SYS),$(TARGET_SYS))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS
|
||||
else
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX
|
||||
else
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(CCDEBUG))
|
||||
TARGET_STRIP= @:
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# Files and pathnames.
|
||||
##############################################################################
|
||||
|
||||
MINILUA_O= host/minilua.o
|
||||
MINILUA_LIBS= -lm
|
||||
MINILUA_T= host/minilua
|
||||
MINILUA_X= $(MINILUA_T)
|
||||
|
||||
ifeq (,$(HOST_LUA))
|
||||
HOST_LUA= $(MINILUA_X)
|
||||
DASM_DEP= $(MINILUA_T)
|
||||
endif
|
||||
|
||||
DASM_DIR= ../dynasm
|
||||
DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua
|
||||
DASM_XFLAGS=
|
||||
DASM_AFLAGS=
|
||||
DASM_ARCH= $(TARGET_LJARCH)
|
||||
|
||||
ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D P64
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D JIT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FFI
|
||||
endif
|
||||
ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D DUALNUM
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FPU
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=1
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=0
|
||||
endif
|
||||
ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D HFABI
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=0
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=1
|
||||
endif
|
||||
DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH))))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D WIN
|
||||
endif
|
||||
ifeq (x86,$(TARGET_LJARCH))
|
||||
ifneq (,$(findstring __SSE2__ 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D SSE
|
||||
endif
|
||||
else
|
||||
ifeq (x64,$(TARGET_LJARCH))
|
||||
DASM_ARCH= x86
|
||||
else
|
||||
ifeq (arm,$(TARGET_LJARCH))
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D IOS
|
||||
endif
|
||||
else
|
||||
ifeq (ppc,$(TARGET_LJARCH))
|
||||
ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D SQRT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D ROUND
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_PPC64 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D GPR64
|
||||
endif
|
||||
ifeq (PS3,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D PPE -D TOC
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS)
|
||||
DASM_DASC= vm_$(DASM_ARCH).dasc
|
||||
|
||||
BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \
|
||||
host/buildvm_lib.o host/buildvm_fold.o
|
||||
BUILDVM_T= host/buildvm
|
||||
BUILDVM_X= $(BUILDVM_T)
|
||||
|
||||
HOST_O= $(MINILUA_O) $(BUILDVM_O)
|
||||
HOST_T= $(MINILUA_T) $(BUILDVM_T)
|
||||
|
||||
LJVM_S= lj_vm.s
|
||||
LJVM_O= lj_vm.o
|
||||
LJVM_BOUT= $(LJVM_S)
|
||||
LJVM_MODE= elfasm
|
||||
|
||||
LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \
|
||||
lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o
|
||||
LJLIB_C= $(LJLIB_O:.o=.c)
|
||||
|
||||
LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o \
|
||||
lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
|
||||
lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \
|
||||
lj_api.o lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \
|
||||
lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
|
||||
lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \
|
||||
lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \
|
||||
lj_asm.o lj_trace.o lj_gdbjit.o \
|
||||
lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \
|
||||
lj_carith.o lj_clib.o lj_cparse.o \
|
||||
lj_lib.o lj_alloc.o lib_aux.o \
|
||||
$(LJLIB_O) lib_init.o
|
||||
|
||||
LJVMCORE_O= $(LJVM_O) $(LJCORE_O)
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o)
|
||||
|
||||
LIB_VMDEF= jit/vmdef.lua
|
||||
LIB_VMDEFP= $(LIB_VMDEF)
|
||||
|
||||
LUAJIT_O= luajit.o
|
||||
LUAJIT_A= libluajit.a
|
||||
LUAJIT_SO= libluajit.so
|
||||
LUAJIT_T= luajit
|
||||
|
||||
ALL_T= $(LUAJIT_T) $(LUAJIT_A) $(LUAJIT_SO) $(HOST_T)
|
||||
ALL_HDRGEN= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \
|
||||
host/buildvm_arch.h
|
||||
ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) $(LIB_VMDEFP)
|
||||
WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest *.pdb *.ilk
|
||||
ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o $(WIN_RM)
|
||||
|
||||
##############################################################################
|
||||
# Build mode handling.
|
||||
##############################################################################
|
||||
|
||||
# Mixed mode defaults.
|
||||
TARGET_O= $(LUAJIT_A)
|
||||
TARGET_T= $(LUAJIT_T) $(LUAJIT_SO)
|
||||
TARGET_DEP= $(LIB_VMDEF) $(LUAJIT_SO)
|
||||
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
TARGET_DYNCC= $(STATIC_CC)
|
||||
LJVM_MODE= coffasm
|
||||
LUAJIT_T= luajit.exe
|
||||
ifeq (cygwin,$(HOST_MSYS))
|
||||
LUAJIT_SO= cyg$(TARGET_DLLNAME)
|
||||
else
|
||||
LUAJIT_SO= $(TARGET_DLLNAME)
|
||||
endif
|
||||
# Mixed mode is not supported on Windows. And static mode doesn't work well.
|
||||
# C modules cannot be loaded, because they bind to lua51.dll.
|
||||
ifneq (static,$(BUILDMODE))
|
||||
BUILDMODE= dynamic
|
||||
TARGET_XCFLAGS+= -DLUA_BUILD_AS_DLL
|
||||
endif
|
||||
endif
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
ifeq (SunOS,$(TARGET_SYS))
|
||||
BUILDMODE= static
|
||||
endif
|
||||
ifeq (PS3,$(TARGET_SYS))
|
||||
BUILDMODE= static
|
||||
endif
|
||||
|
||||
ifeq (Windows,$(HOST_SYS))
|
||||
MINILUA_T= host/minilua.exe
|
||||
BUILDVM_T= host/buildvm.exe
|
||||
ifeq (,$(HOST_MSYS))
|
||||
MINILUA_X= host\minilua
|
||||
BUILDVM_X= host\buildvm
|
||||
ALL_RM:= $(subst /,\,$(ALL_RM))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (static,$(BUILDMODE))
|
||||
TARGET_DYNCC= @:
|
||||
TARGET_T= $(LUAJIT_T)
|
||||
TARGET_DEP= $(LIB_VMDEF)
|
||||
else
|
||||
ifeq (dynamic,$(BUILDMODE))
|
||||
ifneq (Windows,$(TARGET_SYS))
|
||||
TARGET_CC= $(DYNAMIC_CC)
|
||||
endif
|
||||
TARGET_DYNCC= @:
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O)
|
||||
TARGET_O= $(LUAJIT_SO)
|
||||
TARGET_XLDFLAGS+= $(TARGET_DYNXLDOPTS)
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
TARGET_DYNCC= @:
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O)
|
||||
endif
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
TARGET_DYNCC= @:
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
Q= @
|
||||
E= @echo
|
||||
#Q=
|
||||
#E= @:
|
||||
|
||||
##############################################################################
|
||||
# Make targets.
|
||||
##############################################################################
|
||||
|
||||
default all: $(TARGET_T)
|
||||
|
||||
amalg:
|
||||
@grep "^[+|]" ljamalg.c
|
||||
$(MAKE) all "LJCORE_O=ljamalg.o"
|
||||
|
||||
clean:
|
||||
$(HOST_RM) $(ALL_RM)
|
||||
|
||||
depend:
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -f $$file || touch $$file; \
|
||||
done
|
||||
@$(HOST_CC) $(HOST_ACFLAGS) -MM *.c host/*.c | \
|
||||
sed -e "s| [^ ]*/dasm_\S*\.h||g" \
|
||||
-e "s|^\([^l ]\)|host/\1|" \
|
||||
-e "s| lj_target_\S*\.h| lj_target_*.h|g" \
|
||||
-e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \
|
||||
-e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >Makefile.dep
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -s $$file || $(HOST_RM) $$file; \
|
||||
done
|
||||
|
||||
.PHONY: default all amalg clean depend
|
||||
|
||||
##############################################################################
|
||||
# Rules for generated files.
|
||||
##############################################################################
|
||||
|
||||
$(MINILUA_T): $(MINILUA_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(MINILUA_O) $(MINILUA_LIBS) $(HOST_ALIBS)
|
||||
|
||||
host/buildvm_arch.h: $(DASM_DASC) $(DASM_DEP)
|
||||
$(E) "DYNASM $@"
|
||||
$(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC)
|
||||
|
||||
host/buildvm.o: $(DASM_DIR)/dasm_*.h
|
||||
|
||||
$(BUILDVM_T): $(BUILDVM_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(BUILDVM_O) $(HOST_ALIBS)
|
||||
|
||||
$(LJVM_BOUT): $(BUILDVM_T)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@
|
||||
|
||||
lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C)
|
||||
|
||||
lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C)
|
||||
|
||||
lj_libdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C)
|
||||
|
||||
lj_recdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C)
|
||||
|
||||
$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C)
|
||||
|
||||
lj_folddef.h: $(BUILDVM_T) lj_opt_fold.c
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m folddef -o $@ lj_opt_fold.c
|
||||
|
||||
##############################################################################
|
||||
# Object file rules.
|
||||
##############################################################################
|
||||
|
||||
%.o: %.c
|
||||
$(E) "CC $@"
|
||||
$(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $<
|
||||
$(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.s
|
||||
$(E) "ASM $@"
|
||||
$(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $<
|
||||
$(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
$(LUAJIT_O):
|
||||
$(E) "CC $@"
|
||||
$(Q)$(TARGET_STCC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
$(HOST_O): %.o: %.c
|
||||
$(E) "HOSTCC $@"
|
||||
$(Q)$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $<
|
||||
|
||||
include Makefile.dep
|
||||
|
||||
##############################################################################
|
||||
# Target file rules.
|
||||
##############################################################################
|
||||
|
||||
$(LUAJIT_A): $(LJVMCORE_O)
|
||||
$(E) "AR $@"
|
||||
$(Q)$(TARGET_AR) $@ $(LJVMCORE_O)
|
||||
|
||||
# The dependency on _O, but linking with _DYNO is intentional.
|
||||
$(LUAJIT_SO): $(LJVMCORE_O)
|
||||
$(E) "DYNLINK $@"
|
||||
$(Q)$(TARGET_LD) $(TARGET_ASHLDFLAGS) -o $@ $(LJVMCORE_DYNO) $(TARGET_ALIBS)
|
||||
$(Q)$(TARGET_STRIP) $@
|
||||
|
||||
$(LUAJIT_T): $(TARGET_O) $(LUAJIT_O) $(TARGET_DEP)
|
||||
$(E) "LINK $@"
|
||||
$(Q)$(TARGET_LD) $(TARGET_ALDFLAGS) -o $@ $(LUAJIT_O) $(TARGET_O) $(TARGET_ALIBS)
|
||||
$(Q)$(TARGET_STRIP) $@
|
||||
$(E) "OK Successfully built LuaJIT"
|
||||
|
||||
##############################################################################
|
||||
@@ -1,226 +0,0 @@
|
||||
lib_aux.o: lib_aux.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_state.h lj_trace.h lj_jit.h lj_ir.h \
|
||||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_lib.h lj_alloc.h
|
||||
lib_base.o: lib_base.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h \
|
||||
lj_tab.h lj_meta.h lj_state.h lj_ctype.h lj_cconv.h lj_bc.h lj_ff.h \
|
||||
lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h lj_strscan.h \
|
||||
lj_lib.h lj_libdef.h
|
||||
lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_str.h lj_lib.h lj_libdef.h
|
||||
lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_lib.h \
|
||||
lj_libdef.h
|
||||
lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h \
|
||||
lj_ctype.h lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h \
|
||||
lj_ccallback.h lj_clib.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h
|
||||
lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h
|
||||
lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_str.h lj_state.h lj_ff.h lj_ffdef.h \
|
||||
lj_lib.h lj_libdef.h
|
||||
lib_jit.o: lib_jit.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h \
|
||||
lj_obj.h lj_def.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h \
|
||||
lj_bc.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_target.h \
|
||||
lj_target_*.h lj_dispatch.h lj_vm.h lj_vmevent.h lj_lib.h luajit.h \
|
||||
lj_libdef.h
|
||||
lib_math.o: lib_math.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||
lj_def.h lj_arch.h lj_lib.h lj_vm.h lj_libdef.h
|
||||
lib_os.o: lib_os.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h
|
||||
lib_package.o: lib_package.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||
lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h
|
||||
lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h \
|
||||
lj_meta.h lj_state.h lj_ff.h lj_ffdef.h lj_bcdump.h lj_lex.h lj_char.h \
|
||||
lj_lib.h lj_libdef.h
|
||||
lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_lib.h \
|
||||
lj_libdef.h
|
||||
lj_alloc.o: lj_alloc.c lj_def.h lua.h luaconf.h lj_arch.h lj_alloc.h
|
||||
lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h lj_udata.h \
|
||||
lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_vm.h lj_strscan.h
|
||||
lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h \
|
||||
lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h lj_traceerr.h \
|
||||
lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h lj_emit_*.h \
|
||||
lj_asm_*.h
|
||||
lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \
|
||||
lj_bcdef.h
|
||||
lj_bcread.o: lj_bcread.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_bc.h lj_ctype.h \
|
||||
lj_cdata.h lualib.h lj_lex.h lj_bcdump.h lj_state.h
|
||||
lj_bcwrite.o: lj_bcwrite.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_str.h lj_bc.h lj_ctype.h lj_dispatch.h lj_jit.h lj_ir.h \
|
||||
lj_bcdump.h lj_lex.h lj_err.h lj_errmsg.h lj_vm.h
|
||||
lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_meta.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h lj_carith.h
|
||||
lj_ccall.o: lj_ccall.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h lj_ccall.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
|
||||
lj_traceerr.h
|
||||
lj_ccallback.o: lj_ccallback.c lj_obj.h lua.h luaconf.h lj_def.h \
|
||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_state.h lj_frame.h \
|
||||
lj_bc.h lj_ctype.h lj_cconv.h lj_ccall.h lj_ccallback.h lj_target.h \
|
||||
lj_target_*.h lj_mcode.h lj_jit.h lj_ir.h lj_trace.h lj_dispatch.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_gc.h lj_cdata.h lj_cconv.h \
|
||||
lj_ccallback.h
|
||||
lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h
|
||||
lj_char.o: lj_char.c lj_char.h lj_def.h lua.h luaconf.h
|
||||
lj_clib.o: lj_clib.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_tab.h lj_str.h lj_udata.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h lj_clib.h
|
||||
lj_cparse.o: lj_cparse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ctype.h lj_cparse.h lj_frame.h \
|
||||
lj_bc.h lj_vm.h lj_char.h lj_strscan.h
|
||||
lj_crecord.o: lj_crecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h \
|
||||
lj_gc.h lj_cdata.h lj_cparse.h lj_cconv.h lj_clib.h lj_ccall.h lj_ff.h \
|
||||
lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h lj_snap.h \
|
||||
lj_crecord.h
|
||||
lj_ctype.o: lj_ctype.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_ccallback.h
|
||||
lj_debug.o: lj_debug.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_state.h lj_frame.h \
|
||||
lj_bc.h lj_jit.h lj_ir.h
|
||||
lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_func.h lj_str.h lj_tab.h lj_meta.h lj_debug.h \
|
||||
lj_state.h lj_frame.h lj_bc.h lj_ff.h lj_ffdef.h lj_jit.h lj_ir.h \
|
||||
lj_ccallback.h lj_ctype.h lj_gc.h lj_trace.h lj_dispatch.h lj_traceerr.h \
|
||||
lj_vm.h luajit.h
|
||||
lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \
|
||||
lj_errmsg.h lj_debug.h lj_str.h lj_func.h lj_state.h lj_frame.h lj_bc.h \
|
||||
lj_ff.h lj_ffdef.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_ffrecord.o: lj_ffrecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ff.h \
|
||||
lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h lj_crecord.h \
|
||||
lj_vm.h lj_strscan.h lj_recdef.h
|
||||
lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_func.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_udata.h lj_meta.h \
|
||||
lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cdata.h lj_trace.h lj_jit.h \
|
||||
lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h
|
||||
lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_frame.h lj_bc.h lj_jit.h \
|
||||
lj_ir.h lj_dispatch.h
|
||||
lj_ir.o: lj_ir.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_str.h lj_tab.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \
|
||||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_ctype.h lj_cdata.h lj_carith.h \
|
||||
lj_vm.h lj_strscan.h lj_lib.h
|
||||
lj_lex.o: lj_lex.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_cdata.h lualib.h \
|
||||
lj_state.h lj_lex.h lj_parse.h lj_char.h lj_strscan.h
|
||||
lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \
|
||||
lj_dispatch.h lj_jit.h lj_ir.h lj_vm.h lj_strscan.h lj_lib.h
|
||||
lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_func.h lj_frame.h \
|
||||
lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h
|
||||
lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h lj_dispatch.h lj_bc.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \
|
||||
lj_vm.h lj_strscan.h
|
||||
lj_obj.o: lj_obj.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h
|
||||
lj_opt_dce.o: lj_opt_dce.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_ir.h lj_jit.h lj_iropt.h
|
||||
lj_opt_fold.o: lj_opt_fold.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_str.h lj_tab.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \
|
||||
lj_bc.h lj_traceerr.h lj_ctype.h lj_gc.h lj_carith.h lj_vm.h \
|
||||
lj_strscan.h lj_folddef.h
|
||||
lj_opt_loop.o: lj_opt_loop.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h \
|
||||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_snap.h lj_vm.h
|
||||
lj_opt_mem.o: lj_opt_mem.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_tab.h lj_ir.h lj_jit.h lj_iropt.h
|
||||
lj_opt_narrow.o: lj_opt_narrow.c lj_obj.h lua.h luaconf.h lj_def.h \
|
||||
lj_arch.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \
|
||||
lj_traceerr.h lj_vm.h lj_strscan.h
|
||||
lj_opt_sink.o: lj_opt_sink.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_ir.h lj_jit.h lj_iropt.h lj_target.h lj_target_*.h
|
||||
lj_opt_split.o: lj_opt_split.c lj_obj.h lua.h luaconf.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_str.h lj_ir.h lj_jit.h lj_ircall.h \
|
||||
lj_iropt.h lj_vm.h
|
||||
lj_parse.o: lj_parse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h \
|
||||
lj_state.h lj_bc.h lj_ctype.h lj_lex.h lj_parse.h lj_vm.h lj_vmevent.h
|
||||
lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \
|
||||
lj_ctype.h lj_gc.h lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h \
|
||||
lj_iropt.h lj_trace.h lj_dispatch.h lj_traceerr.h lj_record.h \
|
||||
lj_ffrecord.h lj_snap.h lj_vm.h
|
||||
lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_tab.h lj_state.h lj_frame.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h \
|
||||
lj_trace.h lj_dispatch.h lj_traceerr.h lj_snap.h lj_target.h \
|
||||
lj_target_*.h lj_ctype.h lj_cdata.h
|
||||
lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_meta.h \
|
||||
lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_trace.h lj_jit.h lj_ir.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_alloc.h
|
||||
lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_state.h lj_char.h
|
||||
lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_char.h lj_strscan.h
|
||||
lj_tab.o: lj_tab.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_tab.h
|
||||
lj_trace.o: lj_trace.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_frame.h lj_bc.h \
|
||||
lj_state.h lj_ir.h lj_jit.h lj_iropt.h lj_mcode.h lj_trace.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_snap.h lj_gdbjit.h lj_record.h lj_asm.h \
|
||||
lj_vm.h lj_vmevent.h lj_target.h lj_target_*.h
|
||||
lj_udata.o: lj_udata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_udata.h
|
||||
lj_vmevent.o: lj_vmevent.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_str.h lj_tab.h lj_state.h lj_dispatch.h lj_bc.h lj_jit.h lj_ir.h \
|
||||
lj_vm.h lj_vmevent.h
|
||||
lj_vmmath.o: lj_vmmath.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_ir.h lj_vm.h
|
||||
ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h \
|
||||
lj_udata.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cdata.h \
|
||||
lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_err.c \
|
||||
lj_debug.h lj_ff.h lj_ffdef.h lj_char.c lj_char.h lj_bc.c lj_bcdef.h \
|
||||
lj_obj.c lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_strscan.h \
|
||||
lj_debug.c lj_state.c lj_lex.h lj_alloc.h lj_dispatch.c lj_ccallback.h \
|
||||
luajit.h lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c lj_api.c \
|
||||
lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h \
|
||||
lj_bcwrite.c lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c \
|
||||
lj_ccall.c lj_ccall.h lj_ccallback.c lj_target.h lj_target_*.h \
|
||||
lj_mcode.h lj_carith.c lj_carith.h lj_clib.c lj_clib.h lj_cparse.c \
|
||||
lj_cparse.h lj_lib.c lj_lib.h lj_ir.c lj_ircall.h lj_iropt.h \
|
||||
lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c lj_opt_dce.c \
|
||||
lj_opt_loop.c lj_snap.h lj_opt_split.c lj_opt_sink.c lj_mcode.c \
|
||||
lj_snap.c lj_record.c lj_record.h lj_ffrecord.h lj_crecord.c \
|
||||
lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h lj_emit_*.h \
|
||||
lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c \
|
||||
lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c lib_io.c \
|
||||
lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c lib_ffi.c \
|
||||
lib_init.c
|
||||
luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h
|
||||
host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \
|
||||
lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \
|
||||
lj_ircall.h lj_ir.h lj_jit.h lj_frame.h lj_bc.h lj_dispatch.h lj_ctype.h \
|
||||
lj_gc.h lj_ccall.h lj_ctype.h luajit.h \
|
||||
host/buildvm_arch.h lj_traceerr.h
|
||||
host/buildvm_asm.o: host/buildvm_asm.c host/buildvm.h lj_def.h lua.h luaconf.h \
|
||||
lj_arch.h lj_bc.h lj_def.h lj_arch.h
|
||||
host/buildvm_fold.o: host/buildvm_fold.c host/buildvm.h lj_def.h lua.h \
|
||||
luaconf.h lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_ir.h lj_obj.h
|
||||
host/buildvm_lib.o: host/buildvm_lib.c host/buildvm.h lj_def.h lua.h luaconf.h \
|
||||
lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_lib.h lj_obj.h
|
||||
host/buildvm_peobj.o: host/buildvm_peobj.c host/buildvm.h lj_def.h lua.h \
|
||||
luaconf.h lj_arch.h lj_bc.h lj_def.h lj_arch.h
|
||||
host/minilua.o: host/minilua.c
|
||||
@@ -1,368 +0,0 @@
|
||||
/*
|
||||
** LuaJIT VM builder: PE object emitter.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Only used for building on Windows, since we cannot assume the presence
|
||||
** of a suitable assembler. The host and target byte order must match.
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
#if LJ_TARGET_X86ORX64 || LJ_TARGET_PPC
|
||||
|
||||
/* Context for PE object emitter. */
|
||||
static char *strtab;
|
||||
static size_t strtabofs;
|
||||
|
||||
/* -- PE object definitions ----------------------------------------------- */
|
||||
|
||||
/* PE header. */
|
||||
typedef struct PEheader {
|
||||
uint16_t arch;
|
||||
uint16_t nsects;
|
||||
uint32_t time;
|
||||
uint32_t symtabofs;
|
||||
uint32_t nsyms;
|
||||
uint16_t opthdrsz;
|
||||
uint16_t flags;
|
||||
} PEheader;
|
||||
|
||||
/* PE section. */
|
||||
typedef struct PEsection {
|
||||
char name[8];
|
||||
uint32_t vsize;
|
||||
uint32_t vaddr;
|
||||
uint32_t size;
|
||||
uint32_t ofs;
|
||||
uint32_t relocofs;
|
||||
uint32_t lineofs;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
|
||||
/* PE relocation. */
|
||||
typedef struct PEreloc {
|
||||
uint32_t vaddr;
|
||||
uint32_t symidx;
|
||||
uint16_t type;
|
||||
} PEreloc;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_RELOC_SIZE (4+4+2)
|
||||
|
||||
/* PE symbol table entry. */
|
||||
typedef struct PEsym {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
} n;
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl;
|
||||
uint8_t naux;
|
||||
} PEsym;
|
||||
|
||||
/* PE symbol table auxiliary entry for a section. */
|
||||
typedef struct PEsymaux {
|
||||
uint32_t size;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel;
|
||||
uint8_t unused[3];
|
||||
} PEsymaux;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_SYM_SIZE (8+4+2+2+1+1)
|
||||
|
||||
/* PE object CPU specific defines. */
|
||||
#if LJ_TARGET_X86
|
||||
#define PEOBJ_ARCH_TARGET 0x014c
|
||||
#define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x06
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#elif LJ_TARGET_X64
|
||||
#define PEOBJ_ARCH_TARGET 0x8664
|
||||
#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x02
|
||||
#define PEOBJ_RELOC_ADDR32NB 0x03
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#elif LJ_TARGET_PPC
|
||||
#define PEOBJ_ARCH_TARGET 0x01f2
|
||||
#define PEOBJ_RELOC_REL32 0x06
|
||||
#define PEOBJ_RELOC_DIR32 0x02
|
||||
#define PEOBJ_RELOC_OFS (-4)
|
||||
#define PEOBJ_TEXT_FLAGS 0x60400020 /* 60=r+x, 40=align8, 20=code. */
|
||||
#endif
|
||||
|
||||
/* Section numbers (0-based). */
|
||||
enum {
|
||||
PEOBJ_SECT_ABS = -2,
|
||||
PEOBJ_SECT_UNDEF = -1,
|
||||
PEOBJ_SECT_TEXT,
|
||||
#if LJ_TARGET_X64
|
||||
PEOBJ_SECT_PDATA,
|
||||
PEOBJ_SECT_XDATA,
|
||||
#endif
|
||||
PEOBJ_SECT_RDATA_Z,
|
||||
PEOBJ_NSECTIONS
|
||||
};
|
||||
|
||||
/* Symbol types. */
|
||||
#define PEOBJ_TYPE_NULL 0
|
||||
#define PEOBJ_TYPE_FUNC 0x20
|
||||
|
||||
/* Symbol storage class. */
|
||||
#define PEOBJ_SCL_EXTERN 2
|
||||
#define PEOBJ_SCL_STATIC 3
|
||||
|
||||
/* -- PE object emitter --------------------------------------------------- */
|
||||
|
||||
/* Emit PE object symbol. */
|
||||
static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
|
||||
int sect, int type, int scl)
|
||||
{
|
||||
PEsym sym;
|
||||
size_t len = strlen(name);
|
||||
if (!strtab) { /* Pass 1: only calculate string table length. */
|
||||
if (len > 8) strtabofs += len+1;
|
||||
return;
|
||||
}
|
||||
if (len <= 8) {
|
||||
memcpy(sym.n.name, name, len);
|
||||
memset(sym.n.name+len, 0, 8-len);
|
||||
} else {
|
||||
sym.n.nameref[0] = 0;
|
||||
sym.n.nameref[1] = (uint32_t)strtabofs;
|
||||
memcpy(strtab + strtabofs, name, len);
|
||||
strtab[strtabofs+len] = 0;
|
||||
strtabofs += len+1;
|
||||
}
|
||||
sym.value = value;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = (uint16_t)type;
|
||||
sym.scl = (uint8_t)scl;
|
||||
sym.naux = 0;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit PE object section symbol. */
|
||||
static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
|
||||
{
|
||||
PEsym sym;
|
||||
PEsymaux aux;
|
||||
if (!strtab) return; /* Pass 1: no output. */
|
||||
memcpy(sym.n.name, pesect[sect].name, 8);
|
||||
sym.value = 0;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = PEOBJ_TYPE_NULL;
|
||||
sym.scl = PEOBJ_SCL_STATIC;
|
||||
sym.naux = 1;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
memset(&aux, 0, sizeof(PEsymaux));
|
||||
aux.size = pesect[sect].size;
|
||||
aux.nreloc = pesect[sect].nreloc;
|
||||
owrite(ctx, &aux, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit Windows PE object file. */
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
PEheader pehdr;
|
||||
PEsection pesect[PEOBJ_NSECTIONS];
|
||||
uint32_t sofs;
|
||||
int i, nrsym;
|
||||
union { uint8_t b; uint32_t u; } host_endian;
|
||||
|
||||
sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
|
||||
|
||||
/* Fill in PE sections. */
|
||||
memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection));
|
||||
memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1);
|
||||
pesect[PEOBJ_SECT_TEXT].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz);
|
||||
pesect[PEOBJ_SECT_TEXT].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 60 = read+execute, 50 = align16, 20 = code. */
|
||||
pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS;
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1);
|
||||
pesect[PEOBJ_SECT_PDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].size = 6*4);
|
||||
pesect[PEOBJ_SECT_PDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = 6) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_PDATA].flags = 0x40300040;
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1);
|
||||
pesect[PEOBJ_SECT_XDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].size = 8*2+4+6*2); /* See below. */
|
||||
pesect[PEOBJ_SECT_XDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_XDATA].flags = 0x40300040;
|
||||
#endif
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
|
||||
pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
|
||||
|
||||
/* Fill in PE header. */
|
||||
pehdr.arch = PEOBJ_ARCH_TARGET;
|
||||
pehdr.nsects = PEOBJ_NSECTIONS;
|
||||
pehdr.time = 0; /* Timestamp is optional. */
|
||||
pehdr.symtabofs = sofs;
|
||||
pehdr.opthdrsz = 0;
|
||||
pehdr.flags = 0;
|
||||
|
||||
/* Compute the size of the symbol table:
|
||||
** @feat.00 + nsections*2
|
||||
** + asm_start + nsym
|
||||
** + nrsym
|
||||
*/
|
||||
nrsym = ctx->nrelocsym;
|
||||
pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym;
|
||||
#if LJ_TARGET_X64
|
||||
pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win64. */
|
||||
#endif
|
||||
|
||||
/* Write PE object header and all sections. */
|
||||
owrite(ctx, &pehdr, sizeof(PEheader));
|
||||
owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
|
||||
|
||||
/* Write .text section. */
|
||||
host_endian.u = 1;
|
||||
if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
|
||||
#if LJ_TARGET_PPC
|
||||
uint32_t *p = (uint32_t *)ctx->code;
|
||||
int n = (int)(ctx->codesz >> 2);
|
||||
for (i = 0; i < n; i++, p++)
|
||||
*p = lj_bswap(*p); /* Byteswap .text section. */
|
||||
#else
|
||||
fprintf(stderr, "Error: different byte order for host and target\n");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
owrite(ctx, ctx->code, ctx->codesz);
|
||||
for (i = 0; i < ctx->nreloc; i++) {
|
||||
PEreloc reloc;
|
||||
reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS;
|
||||
reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */
|
||||
reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
{ /* Write .pdata section. */
|
||||
uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs;
|
||||
uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */
|
||||
PEreloc reloc;
|
||||
pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
{ /* Write .xdata section. */
|
||||
uint16_t xdata[8+2+6];
|
||||
PEreloc reloc;
|
||||
xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */
|
||||
xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */
|
||||
xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */
|
||||
xdata[3] = 0x3000; /* Push rbx. */
|
||||
xdata[4] = 0x6000; /* Push rsi. */
|
||||
xdata[5] = 0x7000; /* Push rdi. */
|
||||
xdata[6] = 0x5000; /* Push rbp. */
|
||||
xdata[7] = 0; /* Alignment. */
|
||||
xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */
|
||||
xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */
|
||||
xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */
|
||||
xdata[12] = 0x0300; /* set_fpreg. */
|
||||
xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */
|
||||
xdata[14] = 0x3000; /* Push rbx. */
|
||||
xdata[15] = 0x5000; /* Push rbp. */
|
||||
owrite(ctx, &xdata, sizeof(xdata));
|
||||
reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write .rdata$Z section. */
|
||||
owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
|
||||
|
||||
/* Write symbol table. */
|
||||
strtab = NULL; /* 1st pass: collect string sizes. */
|
||||
for (;;) {
|
||||
strtabofs = 4;
|
||||
/* Mark as SafeSEH compliant. */
|
||||
emit_peobj_sym(ctx, "@feat.00", 1,
|
||||
PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
|
||||
for (i = 0; i < nrsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->relocsym[i], 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA);
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA);
|
||||
emit_peobj_sym(ctx, "lj_err_unwind_win64", 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
#endif
|
||||
|
||||
emit_peobj_sym(ctx, ctx->beginsym, 0,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN);
|
||||
for (i = 0; i < ctx->nsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
|
||||
|
||||
if (strtab)
|
||||
break;
|
||||
/* 2nd pass: alloc strtab, write syms and copy strings. */
|
||||
strtab = (char *)malloc(strtabofs);
|
||||
*(uint32_t *)strtab = (uint32_t)strtabofs;
|
||||
}
|
||||
|
||||
/* Write string table. */
|
||||
owrite(ctx, strtab, strtabofs);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
fprintf(stderr, "Error: no PE object support for this target\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,191 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT bytecode listing module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
|
||||
-- it hooks into the parser and lists all functions of a chunk as they
|
||||
-- are parsed.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
|
||||
-- luajit -jbc=- foo.lua
|
||||
-- luajit -jbc=foo.list foo.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
|
||||
-- is started.
|
||||
--
|
||||
-- This module can also be used programmatically:
|
||||
--
|
||||
-- local bc = require("jit.bc")
|
||||
--
|
||||
-- local function foo() print("hello") end
|
||||
--
|
||||
-- bc.dump(foo) --> -- BYTECODE -- [...]
|
||||
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
|
||||
--
|
||||
-- local out = {
|
||||
-- -- Do something with each line:
|
||||
-- write = function(t, ...) io.write(...) end,
|
||||
-- close = function(t) end,
|
||||
-- flush = function(t) end,
|
||||
-- }
|
||||
-- bc.dump(foo, out)
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20001, "LuaJIT core/library version mismatch")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local bit = require("bit")
|
||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
||||
local byte, band, shr = string.byte, bit.band, bit.rshift
|
||||
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
|
||||
local funcuvname = jutil.funcuvname
|
||||
local bcnames = vmdef.bcnames
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
-- Return one bytecode line.
|
||||
local function bcline(func, pc, prefix)
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then return end
|
||||
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
|
||||
local a = band(shr(ins, 8), 0xff)
|
||||
local oidx = 6*band(ins, 0xff)
|
||||
local op = sub(bcnames, oidx+1, oidx+6)
|
||||
local s = format("%04d %s %-6s %3s ",
|
||||
pc, prefix or " ", op, ma == 0 and "" or a)
|
||||
local d = shr(ins, 16)
|
||||
if mc == 13*128 then -- BCMjump
|
||||
return format("%s=> %04d\n", s, pc+d-0x7fff)
|
||||
end
|
||||
if mb ~= 0 then
|
||||
d = band(d, 0xff)
|
||||
elseif mc == 0 then
|
||||
return s.."\n"
|
||||
end
|
||||
local kc
|
||||
if mc == 10*128 then -- BCMstr
|
||||
kc = funck(func, -d-1)
|
||||
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
|
||||
elseif mc == 9*128 then -- BCMnum
|
||||
kc = funck(func, d)
|
||||
if op == "TSETM " then kc = kc - 2^52 end
|
||||
elseif mc == 12*128 then -- BCMfunc
|
||||
local fi = funcinfo(funck(func, -d-1))
|
||||
if fi.ffid then
|
||||
kc = vmdef.ffnames[fi.ffid]
|
||||
else
|
||||
kc = fi.loc
|
||||
end
|
||||
elseif mc == 5*128 then -- BCMuv
|
||||
kc = funcuvname(func, d)
|
||||
end
|
||||
if ma == 5 then -- BCMuv
|
||||
local ka = funcuvname(func, a)
|
||||
if kc then kc = ka.." ; "..kc else kc = ka end
|
||||
end
|
||||
if mb ~= 0 then
|
||||
local b = shr(ins, 24)
|
||||
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
|
||||
return format("%s%3d %3d\n", s, b, d)
|
||||
end
|
||||
if kc then return format("%s%3d ; %s\n", s, d, kc) end
|
||||
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
|
||||
return format("%s%3d\n", s, d)
|
||||
end
|
||||
|
||||
-- Collect branch targets of a function.
|
||||
local function bctargets(func)
|
||||
local target = {}
|
||||
for pc=1,1000000000 do
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then break end
|
||||
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
-- Dump bytecode instructions of a function.
|
||||
local function bcdump(func, out, all)
|
||||
if not out then out = stdout end
|
||||
local fi = funcinfo(func)
|
||||
if all and fi.children then
|
||||
for n=-1,-1000000000,-1 do
|
||||
local k = funck(func, n)
|
||||
if not k then break end
|
||||
if type(k) == "proto" then bcdump(k, out, true) end
|
||||
end
|
||||
end
|
||||
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
|
||||
local target = bctargets(func)
|
||||
for pc=1,1000000000 do
|
||||
local s = bcline(func, pc, target[pc] and "=>")
|
||||
if not s then break end
|
||||
out:write(s)
|
||||
end
|
||||
out:write("\n")
|
||||
out:flush()
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
-- List handler.
|
||||
local function h_list(func)
|
||||
return bcdump(func, out)
|
||||
end
|
||||
|
||||
-- Detach list handler.
|
||||
local function bclistoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(h_list)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach list handler.
|
||||
local function bcliston(outfile)
|
||||
if active then bclistoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(h_list, "bc")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
line = bcline
|
||||
dump = bcdump
|
||||
targets = bctargets
|
||||
|
||||
on = bcliston
|
||||
off = bclistoff
|
||||
start = bcliston -- For -j command line option.
|
||||
|
||||
@@ -1,659 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT module to save/list bytecode.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module saves or lists the bytecode for an input file.
|
||||
-- It's run by the -b command line option.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20001, "LuaJIT core/library version mismatch")
|
||||
local bit = require("bit")
|
||||
|
||||
-- Symbol name prefix for LuaJIT bytecode.
|
||||
local LJBC_PREFIX = "luaJIT_BC_"
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function usage()
|
||||
io.stderr:write[[
|
||||
Save LuaJIT bytecode: luajit -b[options] input output
|
||||
-l Only list bytecode.
|
||||
-s Strip debug info (default).
|
||||
-g Keep debug info.
|
||||
-n name Set module name (default: auto-detect from input name).
|
||||
-t type Set output file type (default: auto-detect from output name).
|
||||
-a arch Override architecture for object files (default: native).
|
||||
-o os Override OS for object files (default: native).
|
||||
-e chunk Use chunk string as input.
|
||||
-- Stop handling options.
|
||||
- Use stdin as input and/or stdout as output.
|
||||
|
||||
File types: c h obj o raw (default)
|
||||
]]
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function check(ok, ...)
|
||||
if ok then return ok, ... end
|
||||
io.stderr:write("luajit: ", ...)
|
||||
io.stderr:write("\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function readfile(input)
|
||||
if type(input) == "function" then return input end
|
||||
if input == "-" then input = nil end
|
||||
return check(loadfile(input))
|
||||
end
|
||||
|
||||
local function savefile(name, mode)
|
||||
if name == "-" then return io.stdout end
|
||||
return check(io.open(name, mode))
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_type = {
|
||||
raw = "raw", c = "c", h = "h", o = "obj", obj = "obj",
|
||||
}
|
||||
|
||||
local map_arch = {
|
||||
x86 = true, x64 = true, arm = true, ppc = true, ppcspe = true,
|
||||
mips = true, mipsel = true,
|
||||
}
|
||||
|
||||
local map_os = {
|
||||
linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
|
||||
openbsd = true, solaris = true,
|
||||
}
|
||||
|
||||
local function checkarg(str, map, err)
|
||||
str = string.lower(str)
|
||||
local s = check(map[str], "unknown ", err)
|
||||
return s == true and str or s
|
||||
end
|
||||
|
||||
local function detecttype(str)
|
||||
local ext = string.match(string.lower(str), "%.(%a+)$")
|
||||
return map_type[ext] or "raw"
|
||||
end
|
||||
|
||||
local function checkmodname(str)
|
||||
check(string.match(str, "^[%w_.%-]+$"), "bad module name")
|
||||
return string.gsub(str, "[%.%-]", "_")
|
||||
end
|
||||
|
||||
local function detectmodname(str)
|
||||
if type(str) == "string" then
|
||||
local tail = string.match(str, "[^/\\]+$")
|
||||
if tail then str = tail end
|
||||
local head = string.match(str, "^(.*)%.[^.]*$")
|
||||
if head then str = head end
|
||||
str = string.match(str, "^[%w_.%-]+")
|
||||
else
|
||||
str = nil
|
||||
end
|
||||
check(str, "cannot derive module name, use -n name")
|
||||
return string.gsub(str, "[%.%-]", "_")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bcsave_tail(fp, output, s)
|
||||
local ok, err = fp:write(s)
|
||||
if ok and output ~= "-" then ok, err = fp:close() end
|
||||
check(ok, "cannot write ", output, ": ", err)
|
||||
end
|
||||
|
||||
local function bcsave_raw(output, s)
|
||||
local fp = savefile(output, "wb")
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_c(ctx, output, s)
|
||||
local fp = savefile(output, "w")
|
||||
if ctx.type == "c" then
|
||||
fp:write(string.format([[
|
||||
#ifdef _cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
const char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname))
|
||||
else
|
||||
fp:write(string.format([[
|
||||
#define %s%s_SIZE %d
|
||||
static const char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
|
||||
end
|
||||
local t, n, m = {}, 0, 0
|
||||
for i=1,#s do
|
||||
local b = tostring(string.byte(s, i))
|
||||
m = m + #b + 1
|
||||
if m > 78 then
|
||||
fp:write(table.concat(t, ",", 1, n), ",\n")
|
||||
n, m = 0, #b + 1
|
||||
end
|
||||
n = n + 1
|
||||
t[n] = b
|
||||
end
|
||||
bcsave_tail(fp, output, table.concat(t, ",", 1, n).."\n};\n")
|
||||
end
|
||||
|
||||
local function bcsave_elfobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint32_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF32header;
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint64_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF64header;
|
||||
typedef struct {
|
||||
uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
|
||||
} ELF32sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, type;
|
||||
uint64_t flags, addr, ofs, size;
|
||||
uint32_t link, info;
|
||||
uint64_t align, entsize;
|
||||
} ELF64sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, value, size;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
} ELF32symbol;
|
||||
typedef struct {
|
||||
uint32_t name;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
uint64_t value, size;
|
||||
} ELF64symbol;
|
||||
typedef struct {
|
||||
ELF32header hdr;
|
||||
ELF32sectheader sect[6];
|
||||
ELF32symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF32obj;
|
||||
typedef struct {
|
||||
ELF64header hdr;
|
||||
ELF64sectheader sect[6];
|
||||
ELF64symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF64obj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local is64, isbe = false, false
|
||||
if ctx.arch == "x64" then
|
||||
is64 = true
|
||||
elseif ctx.arch == "ppc" or ctx.arch == "ppcspe" or ctx.arch == "mips" then
|
||||
isbe = true
|
||||
end
|
||||
|
||||
-- Handle different host/target endianess.
|
||||
local function f32(x) return x end
|
||||
local f16, fofs = f32, f32
|
||||
if ffi.abi("be") ~= isbe then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
if is64 then
|
||||
local two32 = ffi.cast("int64_t", 2^32)
|
||||
function fofs(x) return bit.bswap(x)*two32 end
|
||||
else
|
||||
fofs = f32
|
||||
end
|
||||
end
|
||||
|
||||
-- Create ELF object and fill in header.
|
||||
local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
|
||||
local hdr = o.hdr
|
||||
if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
|
||||
local bf = assert(io.open("/bin/ls", "rb"))
|
||||
local bs = bf:read(9)
|
||||
bf:close()
|
||||
ffi.copy(o, bs, 9)
|
||||
check(hdr.emagic[0] == 127, "no support for writing native object files")
|
||||
else
|
||||
hdr.emagic = "\127ELF"
|
||||
hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
|
||||
end
|
||||
hdr.eclass = is64 and 2 or 1
|
||||
hdr.eendian = isbe and 2 or 1
|
||||
hdr.eversion = 1
|
||||
hdr.type = f16(1)
|
||||
hdr.machine = f16(({ x86=3, x64=62, arm=40, ppc=20, ppcspe=20, mips=8, mipsel=8 })[ctx.arch])
|
||||
if ctx.arch == "mips" or ctx.arch == "mipsel" then
|
||||
hdr.flags = 0x50001006
|
||||
end
|
||||
hdr.version = f32(1)
|
||||
hdr.shofs = fofs(ffi.offsetof(o, "sect"))
|
||||
hdr.ehsize = f16(ffi.sizeof(hdr))
|
||||
hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
|
||||
hdr.shnum = f16(6)
|
||||
hdr.shstridx = f16(2)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
local sofs, ofs = ffi.offsetof(o, "space"), 1
|
||||
for i,name in ipairs{
|
||||
".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
|
||||
} do
|
||||
local sect = o.sect[i]
|
||||
sect.align = fofs(1)
|
||||
sect.name = f32(ofs)
|
||||
ffi.copy(o.space+ofs, name)
|
||||
ofs = ofs + #name+1
|
||||
end
|
||||
o.sect[1].type = f32(2) -- .symtab
|
||||
o.sect[1].link = f32(3)
|
||||
o.sect[1].info = f32(1)
|
||||
o.sect[1].align = fofs(8)
|
||||
o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
|
||||
o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
|
||||
o.sect[1].size = fofs(ffi.sizeof(o.sym))
|
||||
o.sym[1].name = f32(1)
|
||||
o.sym[1].sectidx = f16(4)
|
||||
o.sym[1].size = fofs(#s)
|
||||
o.sym[1].info = 17
|
||||
o.sect[2].type = f32(3) -- .shstrtab
|
||||
o.sect[2].ofs = fofs(sofs)
|
||||
o.sect[2].size = fofs(ofs)
|
||||
o.sect[3].type = f32(3) -- .strtab
|
||||
o.sect[3].ofs = fofs(sofs + ofs)
|
||||
o.sect[3].size = fofs(#symname+1)
|
||||
ffi.copy(o.space+ofs+1, symname)
|
||||
ofs = ofs + #symname + 2
|
||||
o.sect[4].type = f32(1) -- .rodata
|
||||
o.sect[4].flags = fofs(2)
|
||||
o.sect[4].ofs = fofs(sofs + ofs)
|
||||
o.sect[4].size = fofs(#s)
|
||||
o.sect[5].type = f32(1) -- .note.GNU-stack
|
||||
o.sect[5].ofs = fofs(sofs + ofs + #s)
|
||||
|
||||
-- Write ELF object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_peobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint16_t arch, nsects;
|
||||
uint32_t time, symtabofs, nsyms;
|
||||
uint16_t opthdrsz, flags;
|
||||
} PEheader;
|
||||
typedef struct {
|
||||
char name[8];
|
||||
uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
typedef struct __attribute((packed)) {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
};
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl, naux;
|
||||
} PEsym;
|
||||
typedef struct __attribute((packed)) {
|
||||
uint32_t size;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel, unused[3];
|
||||
} PEsymaux;
|
||||
typedef struct {
|
||||
PEheader hdr;
|
||||
PEsection sect[2];
|
||||
// Must be an even number of symbol structs.
|
||||
PEsym sym0;
|
||||
PEsymaux sym0aux;
|
||||
PEsym sym1;
|
||||
PEsymaux sym1aux;
|
||||
PEsym sym2;
|
||||
PEsym sym3;
|
||||
uint32_t strtabsize;
|
||||
uint8_t space[4096];
|
||||
} PEobj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local is64 = false
|
||||
if ctx.arch == "x86" then
|
||||
symname = "_"..symname
|
||||
elseif ctx.arch == "x64" then
|
||||
is64 = true
|
||||
end
|
||||
local symexport = " /EXPORT:"..symname..",DATA "
|
||||
|
||||
-- The file format is always little-endian. Swap if the host is big-endian.
|
||||
local function f32(x) return x end
|
||||
local f16 = f32
|
||||
if ffi.abi("be") then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
end
|
||||
|
||||
-- Create PE object and fill in header.
|
||||
local o = ffi.new("PEobj")
|
||||
local hdr = o.hdr
|
||||
hdr.arch = f16(({ x86=0x14c, x64=0x8664, arm=0x1c0, ppc=0x1f2, mips=0x366, mipsel=0x366 })[ctx.arch])
|
||||
hdr.nsects = f16(2)
|
||||
hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
|
||||
hdr.nsyms = f32(6)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
o.sect[0].name = ".drectve"
|
||||
o.sect[0].size = f32(#symexport)
|
||||
o.sect[0].flags = f32(0x00100a00)
|
||||
o.sym0.sect = f16(1)
|
||||
o.sym0.scl = 3
|
||||
o.sym0.name = ".drectve"
|
||||
o.sym0.naux = 1
|
||||
o.sym0aux.size = f32(#symexport)
|
||||
o.sect[1].name = ".rdata"
|
||||
o.sect[1].size = f32(#s)
|
||||
o.sect[1].flags = f32(0x40300040)
|
||||
o.sym1.sect = f16(2)
|
||||
o.sym1.scl = 3
|
||||
o.sym1.name = ".rdata"
|
||||
o.sym1.naux = 1
|
||||
o.sym1aux.size = f32(#s)
|
||||
o.sym2.sect = f16(2)
|
||||
o.sym2.scl = 2
|
||||
o.sym2.nameref[1] = f32(4)
|
||||
o.sym3.sect = f16(-1)
|
||||
o.sym3.scl = 2
|
||||
o.sym3.value = f32(1)
|
||||
o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
|
||||
ffi.copy(o.space, symname)
|
||||
local ofs = #symname + 1
|
||||
o.strtabsize = f32(ofs + 4)
|
||||
o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
ffi.copy(o.space + ofs, symexport)
|
||||
ofs = ofs + #symexport
|
||||
o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
|
||||
-- Write PE object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_machobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
|
||||
} mach_header;
|
||||
typedef struct
|
||||
{
|
||||
mach_header; uint32_t reserved;
|
||||
} mach_header_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize;
|
||||
char segname[16];
|
||||
uint32_t vmaddr, vmsize, fileoff, filesize;
|
||||
uint32_t maxprot, initprot, nsects, flags;
|
||||
} mach_segment_command;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize;
|
||||
char segname[16];
|
||||
uint64_t vmaddr, vmsize, fileoff, filesize;
|
||||
uint32_t maxprot, initprot, nsects, flags;
|
||||
} mach_segment_command_64;
|
||||
typedef struct {
|
||||
char sectname[16], segname[16];
|
||||
uint32_t addr, size;
|
||||
uint32_t offset, align, reloff, nreloc, flags;
|
||||
uint32_t reserved1, reserved2;
|
||||
} mach_section;
|
||||
typedef struct {
|
||||
char sectname[16], segname[16];
|
||||
uint64_t addr, size;
|
||||
uint32_t offset, align, reloff, nreloc, flags;
|
||||
uint32_t reserved1, reserved2, reserved3;
|
||||
} mach_section_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
|
||||
} mach_symtab_command;
|
||||
typedef struct {
|
||||
int32_t strx;
|
||||
uint8_t type, sect;
|
||||
int16_t desc;
|
||||
uint32_t value;
|
||||
} mach_nlist;
|
||||
typedef struct {
|
||||
uint32_t strx;
|
||||
uint8_t type, sect;
|
||||
uint16_t desc;
|
||||
uint64_t value;
|
||||
} mach_nlist_64;
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic, nfat_arch;
|
||||
} mach_fat_header;
|
||||
typedef struct
|
||||
{
|
||||
uint32_t cputype, cpusubtype, offset, size, align;
|
||||
} mach_fat_arch;
|
||||
typedef struct {
|
||||
struct {
|
||||
mach_header hdr;
|
||||
mach_segment_command seg;
|
||||
mach_section sec;
|
||||
mach_symtab_command sym;
|
||||
} arch[1];
|
||||
mach_nlist sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_obj;
|
||||
typedef struct {
|
||||
struct {
|
||||
mach_header_64 hdr;
|
||||
mach_segment_command_64 seg;
|
||||
mach_section_64 sec;
|
||||
mach_symtab_command sym;
|
||||
} arch[1];
|
||||
mach_nlist_64 sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_obj_64;
|
||||
typedef struct {
|
||||
mach_fat_header fat;
|
||||
mach_fat_arch fat_arch[4];
|
||||
struct {
|
||||
mach_header hdr;
|
||||
mach_segment_command seg;
|
||||
mach_section sec;
|
||||
mach_symtab_command sym;
|
||||
} arch[4];
|
||||
mach_nlist sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_fat_obj;
|
||||
]]
|
||||
local symname = '_'..LJBC_PREFIX..ctx.modname
|
||||
local isfat, is64, align, mobj = false, false, 4, "mach_obj"
|
||||
if ctx.arch == "x64" then
|
||||
is64, align, mobj = true, 8, "mach_obj_64"
|
||||
elseif ctx.arch == "arm" then
|
||||
isfat, mobj = true, "mach_fat_obj"
|
||||
else
|
||||
check(ctx.arch == "x86", "unsupported architecture for OSX")
|
||||
end
|
||||
local function aligned(v, a) return bit.band(v+a-1, -a) end
|
||||
local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE.
|
||||
|
||||
-- Create Mach-O object and fill in header.
|
||||
local o = ffi.new(mobj)
|
||||
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align)
|
||||
local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12,12,12} })[ctx.arch]
|
||||
local cpusubtype = ({ x86={3}, x64={3}, arm={3,6,9,11} })[ctx.arch]
|
||||
if isfat then
|
||||
o.fat.magic = be32(0xcafebabe)
|
||||
o.fat.nfat_arch = be32(#cpusubtype)
|
||||
end
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
for i=0,#cpusubtype-1 do
|
||||
local ofs = 0
|
||||
if isfat then
|
||||
local a = o.fat_arch[i]
|
||||
a.cputype = be32(cputype[i+1])
|
||||
a.cpusubtype = be32(cpusubtype[i+1])
|
||||
-- Subsequent slices overlap each other to share data.
|
||||
ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0])
|
||||
a.offset = be32(ofs)
|
||||
a.size = be32(mach_size-ofs+#s)
|
||||
end
|
||||
local a = o.arch[i]
|
||||
a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface
|
||||
a.hdr.cputype = cputype[i+1]
|
||||
a.hdr.cpusubtype = cpusubtype[i+1]
|
||||
a.hdr.filetype = 1
|
||||
a.hdr.ncmds = 2
|
||||
a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym)
|
||||
a.seg.cmd = is64 and 0x19 or 0x1
|
||||
a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)
|
||||
a.seg.vmsize = #s
|
||||
a.seg.fileoff = mach_size-ofs
|
||||
a.seg.filesize = #s
|
||||
a.seg.maxprot = 1
|
||||
a.seg.initprot = 1
|
||||
a.seg.nsects = 1
|
||||
ffi.copy(a.sec.sectname, "__data")
|
||||
ffi.copy(a.sec.segname, "__DATA")
|
||||
a.sec.size = #s
|
||||
a.sec.offset = mach_size-ofs
|
||||
a.sym.cmd = 2
|
||||
a.sym.cmdsize = ffi.sizeof(a.sym)
|
||||
a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs
|
||||
a.sym.nsyms = 1
|
||||
a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs
|
||||
a.sym.strsize = aligned(#symname+2, align)
|
||||
end
|
||||
o.sym_entry.type = 0xf
|
||||
o.sym_entry.sect = 1
|
||||
o.sym_entry.strx = 1
|
||||
ffi.copy(o.space+1, symname)
|
||||
|
||||
-- Write Macho-O object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, mach_size))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_obj(ctx, output, s)
|
||||
local ok, ffi = pcall(require, "ffi")
|
||||
check(ok, "FFI library required to write this file type")
|
||||
if ctx.os == "windows" then
|
||||
return bcsave_peobj(ctx, output, s, ffi)
|
||||
elseif ctx.os == "osx" then
|
||||
return bcsave_machobj(ctx, output, s, ffi)
|
||||
else
|
||||
return bcsave_elfobj(ctx, output, s, ffi)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bclist(input, output)
|
||||
local f = readfile(input)
|
||||
require("jit.bc").dump(f, savefile(output, "w"), true)
|
||||
end
|
||||
|
||||
local function bcsave(ctx, input, output)
|
||||
local f = readfile(input)
|
||||
local s = string.dump(f, ctx.strip)
|
||||
local t = ctx.type
|
||||
if not t then
|
||||
t = detecttype(output)
|
||||
ctx.type = t
|
||||
end
|
||||
if t == "raw" then
|
||||
bcsave_raw(output, s)
|
||||
else
|
||||
if not ctx.modname then ctx.modname = detectmodname(input) end
|
||||
if t == "obj" then
|
||||
bcsave_obj(ctx, output, s)
|
||||
else
|
||||
bcsave_c(ctx, output, s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function docmd(...)
|
||||
local arg = {...}
|
||||
local n = 1
|
||||
local list = false
|
||||
local ctx = {
|
||||
strip = true, arch = jit.arch, os = string.lower(jit.os),
|
||||
type = false, modname = false,
|
||||
}
|
||||
while n <= #arg do
|
||||
local a = arg[n]
|
||||
if type(a) == "string" and string.sub(a, 1, 1) == "-" and a ~= "-" then
|
||||
table.remove(arg, n)
|
||||
if a == "--" then break end
|
||||
for m=2,#a do
|
||||
local opt = string.sub(a, m, m)
|
||||
if opt == "l" then
|
||||
list = true
|
||||
elseif opt == "s" then
|
||||
ctx.strip = true
|
||||
elseif opt == "g" then
|
||||
ctx.strip = false
|
||||
else
|
||||
if arg[n] == nil or m ~= #a then usage() end
|
||||
if opt == "e" then
|
||||
if n ~= 1 then usage() end
|
||||
arg[1] = check(loadstring(arg[1]))
|
||||
elseif opt == "n" then
|
||||
ctx.modname = checkmodname(table.remove(arg, n))
|
||||
elseif opt == "t" then
|
||||
ctx.type = checkarg(table.remove(arg, n), map_type, "file type")
|
||||
elseif opt == "a" then
|
||||
ctx.arch = checkarg(table.remove(arg, n), map_arch, "architecture")
|
||||
elseif opt == "o" then
|
||||
ctx.os = checkarg(table.remove(arg, n), map_os, "OS name")
|
||||
else
|
||||
usage()
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
if list then
|
||||
if #arg == 0 or #arg > 2 then usage() end
|
||||
bclist(arg[1], arg[2] or "-")
|
||||
else
|
||||
if #arg ~= 2 then usage() end
|
||||
bcsave(ctx, arg[1], arg[2])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
start = docmd -- Process -b command line option.
|
||||
|
||||
@@ -1,689 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT ARM disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles most user-mode ARMv7 instructions
|
||||
-- NYI: Advanced SIMD and VFP instructions.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_loadc = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFmDN", "vstmFNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrFdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrFdl", "vldmdbFNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
[11] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGmDN", "vstmGNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrGdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrGdl", "vldmdbGNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
_ = {
|
||||
shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfps = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaF.dnm", "vmlsF.dnm",
|
||||
[0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
|
||||
[0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
|
||||
[0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
|
||||
[0x20000] = "vdivF.dnm",
|
||||
[0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
|
||||
[0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
|
||||
[0x2c000] = "vmovF.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovF.dm", "vabsF.dm",
|
||||
[0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
|
||||
[0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
|
||||
[0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
|
||||
[0x0e01] = "vcvtG.dF.m",
|
||||
[0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
|
||||
[0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
|
||||
[0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfpd = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaG.dnm", "vmlsG.dnm",
|
||||
[0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
|
||||
[0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
|
||||
[0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
|
||||
[0x20000] = "vdivG.dnm",
|
||||
[0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
|
||||
[0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
|
||||
[0x2c000] = "vmovG.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovG.dm", "vabsG.dm",
|
||||
[0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
|
||||
[0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
|
||||
[0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
|
||||
[0x0e01] = "vcvtF.dG.m",
|
||||
[0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
|
||||
[0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
|
||||
[0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
|
||||
},
|
||||
}
|
||||
|
||||
local map_datac = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = map_vfps,
|
||||
[11] = map_vfpd,
|
||||
-- NYI cdp, mcr, mrc.
|
||||
},
|
||||
{
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 15,
|
||||
[0] = "vmovFnD", "vmovFDn",
|
||||
[14] = "vmsrD",
|
||||
[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
|
||||
},
|
||||
},
|
||||
},
|
||||
"svcT",
|
||||
}
|
||||
|
||||
local map_loadcu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP load/store.
|
||||
}
|
||||
|
||||
local map_datacu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP data.
|
||||
}
|
||||
|
||||
local map_simddata = {
|
||||
shift = 0, mask = 0, -- NYI SIMD data.
|
||||
}
|
||||
|
||||
local map_simdload = {
|
||||
shift = 0, mask = 0, -- NYI SIMD load/store, preload.
|
||||
}
|
||||
|
||||
local map_preload = {
|
||||
shift = 0, mask = 0, -- NYI preload.
|
||||
}
|
||||
|
||||
local map_media = {
|
||||
shift = 20, mask = 31,
|
||||
[0] = false,
|
||||
{ --01
|
||||
shift = 5, mask = 7,
|
||||
[0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
|
||||
"sadd8DNM", false, false, "ssub8DNM",
|
||||
},
|
||||
{ --02
|
||||
shift = 5, mask = 7,
|
||||
[0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
|
||||
"qadd8DNM", false, false, "qsub8DNM",
|
||||
},
|
||||
{ --03
|
||||
shift = 5, mask = 7,
|
||||
[0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
|
||||
"shadd8DNM", false, false, "shsub8DNM",
|
||||
},
|
||||
false,
|
||||
{ --05
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
|
||||
"uadd8DNM", false, false, "usub8DNM",
|
||||
},
|
||||
{ --06
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
|
||||
"uqadd8DNM", false, false, "uqsub8DNM",
|
||||
},
|
||||
{ --07
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
|
||||
"uhadd8DNM", false, false, "uhsub8DNM",
|
||||
},
|
||||
{ --08
|
||||
shift = 5, mask = 7,
|
||||
[0] = "pkhbtDNMU", false, "pkhtbDNMU",
|
||||
{ shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
|
||||
"pkhbtDNMU", "selDNM", "pkhtbDNMU",
|
||||
},
|
||||
false,
|
||||
{ --0a
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
|
||||
"ssatDxMu", false, "ssatDxMu",
|
||||
},
|
||||
{ --0b
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "revDM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
|
||||
"ssatDxMu", "rev16DM", "ssatDxMu",
|
||||
},
|
||||
{ --0c
|
||||
shift = 5, mask = 7,
|
||||
[3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
|
||||
},
|
||||
false,
|
||||
{ --0e
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "usat16DwM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
|
||||
"usatDwMu", false, "usatDwMu",
|
||||
},
|
||||
{ --0f
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "rbitDM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
|
||||
"usatDwMu", "revshDM", "usatDwMu",
|
||||
},
|
||||
{ --10
|
||||
shift = 12, mask = 15,
|
||||
[15] = {
|
||||
shift = 5, mask = 7,
|
||||
"smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
|
||||
},
|
||||
_ = {
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
|
||||
},
|
||||
},
|
||||
false, false, false,
|
||||
{ --14
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
|
||||
},
|
||||
{ --15
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
|
||||
{ shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
|
||||
false, false, false, false,
|
||||
"smmlsNMSD", "smmlsrNMSD",
|
||||
},
|
||||
false, false,
|
||||
{ --18
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
|
||||
},
|
||||
false,
|
||||
{ --1a
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1b
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1c
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1d
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1e
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
{ --1f
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
}
|
||||
|
||||
local map_load = {
|
||||
shift = 21, mask = 9,
|
||||
{
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
|
||||
}
|
||||
}
|
||||
|
||||
local map_load1 = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = map_load, map_media,
|
||||
}
|
||||
|
||||
local map_loadm = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "stmdaNR", "stmNR",
|
||||
{ shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
|
||||
"ldmdbNR", "ldmibNR",
|
||||
},
|
||||
}
|
||||
|
||||
local map_data = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
|
||||
"addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
|
||||
"tstNP", "teqNP", "cmpNP", "cmnNP",
|
||||
"orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
|
||||
}
|
||||
|
||||
local map_mul = {
|
||||
shift = 21, mask = 7,
|
||||
[0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
|
||||
"umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
|
||||
}
|
||||
|
||||
local map_sync = {
|
||||
shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
|
||||
[0] = "swpDMN", false, false, false,
|
||||
"swpbDMN", false, false, false,
|
||||
"strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
|
||||
"strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
|
||||
}
|
||||
|
||||
local map_mulh = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = { shift = 5, mask = 3,
|
||||
[0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
|
||||
}
|
||||
|
||||
local map_misc = {
|
||||
shift = 4, mask = 7,
|
||||
-- NYI: decode PSR bits of msr.
|
||||
[0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
|
||||
{ shift = 21, mask = 3, "bxM", false, "clzDM", },
|
||||
{ shift = 21, mask = 3, "bxjM", },
|
||||
{ shift = 21, mask = 3, "blxM", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, "bkptK", },
|
||||
}
|
||||
|
||||
local map_datar = {
|
||||
shift = 4, mask = 9,
|
||||
[9] = {
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
|
||||
{ shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
|
||||
{ shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
|
||||
{ shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 25,
|
||||
[16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
|
||||
_ = {
|
||||
shift = 0, mask = 0xffffffff,
|
||||
[bor(0xe1a00000)] = "nop",
|
||||
_ = map_data,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
local map_datai = {
|
||||
shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
|
||||
[16] = "movwDW", [20] = "movtDW",
|
||||
[18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
|
||||
[22] = "msrNW",
|
||||
_ = map_data,
|
||||
}
|
||||
|
||||
local map_branch = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = "bB", "blB"
|
||||
}
|
||||
|
||||
local map_condins = {
|
||||
[0] = map_datar, map_datai, map_load, map_load1,
|
||||
map_loadm, map_branch, map_loadc, map_datac
|
||||
}
|
||||
|
||||
-- NYI: setend.
|
||||
local map_uncondins = {
|
||||
[0] = false, map_simddata, map_simdload, map_preload,
|
||||
false, "blxB", map_loadcu, map_datacu,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||
}
|
||||
|
||||
local map_cond = {
|
||||
[0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
|
||||
"hi", "ls", "ge", "lt", "gt", "le", "al",
|
||||
}
|
||||
|
||||
local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then
|
||||
extra = "\t->"..sym
|
||||
elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
|
||||
extra = "\t; 0x"..tohex(ctx.rel)
|
||||
end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-5s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-5s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Format operand 2 of load/store opcodes.
|
||||
local function fmtload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local x, ofs
|
||||
local ext = (band(op, 0x04000000) == 0)
|
||||
if not ext and band(op, 0x02000000) == 0 then
|
||||
ofs = band(op, 4095)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
elseif ext and band(op, 0x00400000) ~= 0 then
|
||||
ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
else
|
||||
ofs = map_gpr[band(op, 15)]
|
||||
if ext or band(op, 0xfe0) == 0 then
|
||||
elseif band(op, 0xfe0) == 0x60 then
|
||||
ofs = format("%s, rrx", ofs)
|
||||
else
|
||||
local sh = band(rshift(op, 7), 31)
|
||||
if sh == 0 then sh = 32 end
|
||||
ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
|
||||
end
|
||||
if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
|
||||
end
|
||||
if ofs == "#0" then
|
||||
x = format("[%s]", base)
|
||||
elseif band(op, 0x01000000) == 0 then
|
||||
x = format("[%s], %s", base, ofs)
|
||||
else
|
||||
x = format("[%s, %s]", base, ofs)
|
||||
end
|
||||
if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
|
||||
return x
|
||||
end
|
||||
|
||||
-- Format operand 2 of vector load/store opcodes.
|
||||
local function fmtvload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local ofs = band(op, 255)*4
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
if ofs == 0 then
|
||||
return format("[%s]", base)
|
||||
else
|
||||
return format("[%s, #%d]", base, ofs)
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtvr(op, vr, sh0, sh1)
|
||||
if vr == "s" then
|
||||
return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
|
||||
else
|
||||
return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
|
||||
end
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
local operands = {}
|
||||
local suffix = ""
|
||||
local last, name, pat
|
||||
local vr
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local cond = rshift(op, 28)
|
||||
local opat
|
||||
if cond == 15 then
|
||||
opat = map_uncondins[band(rshift(op, 25), 7)]
|
||||
else
|
||||
if cond ~= 14 then suffix = map_cond[cond] end
|
||||
opat = map_condins[band(rshift(op, 25), 7)]
|
||||
end
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
name, pat = match(opat, "^([a-z0-9]*)(.*)")
|
||||
if sub(pat, 1, 1) == "." then
|
||||
local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
|
||||
suffix = suffix..s2
|
||||
pat = p2
|
||||
end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "D" then
|
||||
x = map_gpr[band(rshift(op, 12), 15)]
|
||||
elseif p == "N" then
|
||||
x = map_gpr[band(rshift(op, 16), 15)]
|
||||
elseif p == "S" then
|
||||
x = map_gpr[band(rshift(op, 8), 15)]
|
||||
elseif p == "M" then
|
||||
x = map_gpr[band(op, 15)]
|
||||
elseif p == "d" then
|
||||
x = fmtvr(op, vr, 12, 22)
|
||||
elseif p == "n" then
|
||||
x = fmtvr(op, vr, 16, 7)
|
||||
elseif p == "m" then
|
||||
x = fmtvr(op, vr, 0, 5)
|
||||
elseif p == "P" then
|
||||
if band(op, 0x02000000) ~= 0 then
|
||||
x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
|
||||
else
|
||||
x = map_gpr[band(op, 15)]
|
||||
if band(op, 0xff0) ~= 0 then
|
||||
operands[#operands+1] = x
|
||||
local s = map_shift[band(rshift(op, 5), 3)]
|
||||
local r = nil
|
||||
if band(op, 0xf90) == 0 then
|
||||
if s == "ror" then s = "rrx" else r = "#32" end
|
||||
elseif band(op, 0x10) == 0 then
|
||||
r = "#"..band(rshift(op, 7), 31)
|
||||
else
|
||||
r = map_gpr[band(rshift(op, 8), 15)]
|
||||
end
|
||||
if name == "mov" then name = s; x = r
|
||||
elseif r then x = format("%s %s", s, r)
|
||||
else x = s end
|
||||
end
|
||||
end
|
||||
elseif p == "L" then
|
||||
x = fmtload(ctx, op, pos)
|
||||
elseif p == "l" then
|
||||
x = fmtvload(ctx, op, pos)
|
||||
elseif p == "B" then
|
||||
local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
|
||||
if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
|
||||
ctx.rel = addr
|
||||
x = "0x"..tohex(addr)
|
||||
elseif p == "F" then
|
||||
vr = "s"
|
||||
elseif p == "G" then
|
||||
vr = "d"
|
||||
elseif p == "." then
|
||||
suffix = suffix..(vr == "s" and ".f32" or ".f64")
|
||||
elseif p == "R" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 1 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local t = {}
|
||||
for i=0,15 do
|
||||
if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
|
||||
end
|
||||
x = "{"..concat(t, ", ").."}"
|
||||
elseif p == "r" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 2 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local s = tonumber(sub(last, 2))
|
||||
local n = band(op, 255)
|
||||
if vr == "d" then n = rshift(n, 1) end
|
||||
operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
|
||||
elseif p == "W" then
|
||||
x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
|
||||
elseif p == "T" then
|
||||
x = "#0x"..tohex(band(op, 0x00ffffff), 6)
|
||||
elseif p == "U" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "u" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if band(op, 0x40) == 0 then
|
||||
if x == 0 then x = nil else x = "lsl #"..x end
|
||||
else
|
||||
if x == 0 then x = "asr #32" else x = "asr #"..x end
|
||||
end
|
||||
elseif p == "v" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
elseif p == "w" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "x" then
|
||||
x = band(rshift(op, 16), 31) + 1
|
||||
elseif p == "X" then
|
||||
x = band(rshift(op, 16), 31) - last + 1
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
|
||||
elseif p == "K" then
|
||||
x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
|
||||
elseif p == "s" then
|
||||
if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then
|
||||
last = x
|
||||
if type(x) == "number" then x = "#"..x end
|
||||
operands[#operands+1] = x
|
||||
end
|
||||
end
|
||||
|
||||
return putop(ctx, name..suffix, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
ctx.pos = ofs
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create_(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass_(code, addr, out)
|
||||
create_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 16 then return map_gpr[r] end
|
||||
return "d"..(r-16)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
disass = disass_
|
||||
regname = regname_
|
||||
|
||||
@@ -1,428 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all standard MIPS32R1/R2 instructions.
|
||||
-- Default mode is big-endian, but see: dis_mipsel.lua
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
|
||||
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
|
||||
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
|
||||
|
||||
local map_special = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
|
||||
map_movci, map_srl, "sraDTA",
|
||||
"sllvDTS", false, map_srlv, "sravDTS",
|
||||
"jrS", "jalrD1S", "movzDST", "movnDST",
|
||||
"syscallY", "breakY", false, "sync",
|
||||
"mfhiD", "mthiS", "mfloD", "mtloS",
|
||||
false, false, false, false,
|
||||
"multST", "multuST", "divST", "divuST",
|
||||
false, false, false, false,
|
||||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
|
||||
"andDST", "orDST", "xorDST", "nor|notDST0",
|
||||
false, false, "sltDST", "sltuDST",
|
||||
false, false, false, false,
|
||||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
|
||||
"teqSTZ", false, "tneSTZ",
|
||||
}
|
||||
|
||||
local map_special2 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "maddST", "madduST", "mulDST", false,
|
||||
"msubST", "msubuST",
|
||||
[32] = "clzDS", [33] = "cloDS",
|
||||
[63] = "sdbbpY",
|
||||
}
|
||||
|
||||
local map_bshfl = {
|
||||
shift = 6, mask = 31,
|
||||
[2] = "wsbhDT",
|
||||
[16] = "sebDT",
|
||||
[24] = "sehDT",
|
||||
}
|
||||
|
||||
local map_special3 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "extTSAK", [4] = "insTSAL",
|
||||
[32] = map_bshfl,
|
||||
[59] = "rdhwrTD",
|
||||
}
|
||||
|
||||
local map_regimm = {
|
||||
shift = 16, mask = 31,
|
||||
[0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
|
||||
false, false, false, false,
|
||||
"tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
|
||||
"teqiSI", false, "tneiSI", false,
|
||||
"bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, "synciSO",
|
||||
}
|
||||
|
||||
local map_cop0 = {
|
||||
shift = 25, mask = 1,
|
||||
[0] = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "mfc0TDW", [4] = "mtc0TDW",
|
||||
[10] = "rdpgprDT",
|
||||
[11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
|
||||
[14] = "wrpgprDT",
|
||||
}, {
|
||||
shift = 0, mask = 63,
|
||||
[1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
|
||||
[24] = "eret", [31] = "deret",
|
||||
[32] = "wait",
|
||||
},
|
||||
}
|
||||
|
||||
local map_cop1s = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
|
||||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
|
||||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
|
||||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
|
||||
"movz.sFGT", "movn.sFGT",
|
||||
false, "recip.sFG", "rsqrt.sFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, "cvt.d.sFG", false, false,
|
||||
"cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
|
||||
"c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
|
||||
"c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
|
||||
"c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
|
||||
}
|
||||
|
||||
local map_cop1d = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
|
||||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
|
||||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
|
||||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
|
||||
"movz.dFGT", "movn.dFGT",
|
||||
false, "recip.dFG", "rsqrt.dFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.dFG", false, false, false,
|
||||
"cvt.w.dFG", "cvt.l.dFG", false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
|
||||
"c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
|
||||
"c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
|
||||
"c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
|
||||
}
|
||||
|
||||
local map_cop1ps = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
|
||||
false, "abs.psFG", "mov.psFG", "neg.psFG",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
|
||||
"movz.psFGT", "movn.psFGT",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.puFG", false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.plFG", false, false, false,
|
||||
"pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
|
||||
"c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
|
||||
"c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
|
||||
"c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
|
||||
"c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
|
||||
}
|
||||
|
||||
local map_cop1w = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
|
||||
}
|
||||
|
||||
local map_cop1l = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
|
||||
}
|
||||
|
||||
local map_cop1bc = {
|
||||
shift = 16, mask = 3,
|
||||
[0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
|
||||
}
|
||||
|
||||
local map_cop1 = {
|
||||
shift = 21, mask = 31,
|
||||
[0] = "mfc1TG", false, "cfc1TG", "mfhc1TG",
|
||||
"mtc1TG", false, "ctc1TG", "mthc1TG",
|
||||
map_cop1bc, false, false, false,
|
||||
false, false, false, false,
|
||||
map_cop1s, map_cop1d, false, false,
|
||||
map_cop1w, map_cop1l, map_cop1ps,
|
||||
}
|
||||
|
||||
local map_cop1x = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "lwxc1FSX", "ldxc1FSX", false, false,
|
||||
false, "luxc1FSX", false, false,
|
||||
"swxc1FSX", "sdxc1FSX", false, false,
|
||||
false, "suxc1FSX", false, "prefxMSX",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, "alnv.psFGHS", false,
|
||||
"madd.sFRGH", "madd.dFRGH", false, false,
|
||||
false, false, "madd.psFRGH", false,
|
||||
"msub.sFRGH", "msub.dFRGH", false, false,
|
||||
false, false, "msub.psFRGH", false,
|
||||
"nmadd.sFRGH", "nmadd.dFRGH", false, false,
|
||||
false, false, "nmadd.psFRGH", false,
|
||||
"nmsub.sFRGH", "nmsub.dFRGH", false, false,
|
||||
false, false, "nmsub.psFRGH", false,
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = map_special, map_regimm, "jJ", "jalJ",
|
||||
"beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
|
||||
"addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
|
||||
"andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
|
||||
map_cop0, map_cop1, false, map_cop1x,
|
||||
"beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
|
||||
false, false, false, false,
|
||||
map_special2, false, false, map_special3,
|
||||
"lbTSO", "lhTSO", "lwlTSO", "lwTSO",
|
||||
"lbuTSO", "lhuTSO", "lwrTSO", false,
|
||||
"sbTSO", "shTSO", "swlTSO", "swTSO",
|
||||
false, false, "swrTSO", "cacheNSO",
|
||||
"llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
|
||||
false, "ldc1HSO", "ldc2TSO", false,
|
||||
"scTSO", "swc1HSO", "swc2TSO", false,
|
||||
false, "sdc1HSO", "sdc2TSO", false,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
local function get_be(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
end
|
||||
|
||||
local function get_le(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local op = ctx:get()
|
||||
local operands = {}
|
||||
local last = nil
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = map_pri[rshift(op, 26)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "S" then
|
||||
x = map_gpr[band(rshift(op, 21), 31)]
|
||||
elseif p == "T" then
|
||||
x = map_gpr[band(rshift(op, 16), 31)]
|
||||
elseif p == "D" then
|
||||
x = map_gpr[band(rshift(op, 11), 31)]
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, 6), 31)
|
||||
elseif p == "G" then
|
||||
x = "f"..band(rshift(op, 11), 31)
|
||||
elseif p == "H" then
|
||||
x = "f"..band(rshift(op, 16), 31)
|
||||
elseif p == "R" then
|
||||
x = "f"..band(rshift(op, 21), 31)
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, 6), 31)
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, 11), 31)
|
||||
elseif p == "N" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "C" then
|
||||
x = band(rshift(op, 18), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "K" then
|
||||
x = band(rshift(op, 11), 31) + 1
|
||||
elseif p == "L" then
|
||||
x = band(rshift(op, 11), 31) - last + 1
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "O" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "X" then
|
||||
local index = map_gpr[band(rshift(op, 16), 31)]
|
||||
operands[#operands] = format("%s(%s)", index, last)
|
||||
elseif p == "B" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 16)*4 + 4
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "J" then
|
||||
x = band(ctx.addr + ctx.pos, 0xf0000000) + band(op, 0x03ffffff)*4
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "V" then
|
||||
x = band(rshift(op, 8), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "W" then
|
||||
x = band(op, 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 6), 0x000fffff)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, 6), 1023)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "0" then
|
||||
if last == "r0" or last == 0 then
|
||||
local n = #operands
|
||||
operands[n] = nil
|
||||
last = operands[n-1]
|
||||
if altname then
|
||||
local a1, a2 = match(altname, "([^|]*)|(.*)")
|
||||
if a1 then name, altname = a1, a2
|
||||
else name = altname end
|
||||
end
|
||||
end
|
||||
elseif p == "1" then
|
||||
if last == "ra" then
|
||||
operands[#operands] = nil
|
||||
end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create_(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
ctx.get = get_be
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_el_(code, addr, out)
|
||||
local ctx = create_(code, addr, out)
|
||||
ctx.get = get_le
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass_(code, addr, out)
|
||||
create_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_el_(code, addr, out)
|
||||
create_el_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
create_el = create_el_
|
||||
disass = disass_
|
||||
disass_el = disass_el_
|
||||
regname = regname_
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPSEL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local require = require
|
||||
|
||||
module(...)
|
||||
|
||||
local dis_mips = require(_PACKAGE.."dis_mips")
|
||||
|
||||
create = dis_mips.create_el
|
||||
disass = dis_mips.disass_el
|
||||
regname = dis_mips.regname
|
||||
|
||||
@@ -1,591 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT PPC disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
|
||||
-- plus the e500 SPE instructions and some Cell/Xenon extensions.
|
||||
--
|
||||
-- NYI: VMX, VMX128
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_crops = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "mcrfXX",
|
||||
[33] = "crnor|crnotCCC=", [129] = "crandcCCC",
|
||||
[193] = "crxor|crclrCCC%", [225] = "crnandCCC",
|
||||
[257] = "crandCCC", [289] = "creqv|crsetCCC%",
|
||||
[417] = "crorcCCC", [449] = "cror|crmoveCCC=",
|
||||
[16] = "b_lrKB", [528] = "b_ctrKB",
|
||||
[150] = "isync",
|
||||
}
|
||||
|
||||
local map_rlwinm = setmetatable({
|
||||
shift = 0, mask = -1,
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
local rot = band(rshift(x, 11), 31)
|
||||
local mb = band(rshift(x, 6), 31)
|
||||
local me = band(rshift(x, 1), 31)
|
||||
if mb == 0 and me == 31-rot then
|
||||
return "slwiRR~A."
|
||||
elseif me == 31 and mb == 32-rot then
|
||||
return "srwiRR~-A."
|
||||
else
|
||||
return "rlwinmRR~AAA."
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local map_rld = {
|
||||
shift = 2, mask = 7,
|
||||
[0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
|
||||
{
|
||||
shift = 1, mask = 1,
|
||||
[0] = "rldclRR~RM.", "rldcrRR~RM.",
|
||||
},
|
||||
}
|
||||
|
||||
local map_ext = setmetatable({
|
||||
shift = 1, mask = 1023,
|
||||
|
||||
[0] = "cmp_YLRR", [32] = "cmpl_YLRR",
|
||||
[4] = "twARR", [68] = "tdARR",
|
||||
|
||||
[8] = "subfcRRR.", [40] = "subfRRR.",
|
||||
[104] = "negRR.", [136] = "subfeRRR.",
|
||||
[200] = "subfzeRR.", [232] = "subfmeRR.",
|
||||
[520] = "subfcoRRR.", [552] = "subfoRRR.",
|
||||
[616] = "negoRR.", [648] = "subfeoRRR.",
|
||||
[712] = "subfzeoRR.", [744] = "subfmeoRR.",
|
||||
|
||||
[9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
|
||||
[457] = "divduRRR.", [489] = "divdRRR.",
|
||||
[745] = "mulldoRRR.",
|
||||
[969] = "divduoRRR.", [1001] = "divdoRRR.",
|
||||
|
||||
[10] = "addcRRR.", [138] = "addeRRR.",
|
||||
[202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
|
||||
[522] = "addcoRRR.", [650] = "addeoRRR.",
|
||||
[714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
|
||||
|
||||
[11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
|
||||
[459] = "divwuRRR.", [491] = "divwRRR.",
|
||||
[747] = "mullwoRRR.",
|
||||
[971] = "divwouRRR.", [1003] = "divwoRRR.",
|
||||
|
||||
[15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
|
||||
|
||||
[144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
|
||||
[19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
|
||||
[371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
|
||||
[339] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
|
||||
},
|
||||
[467] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
|
||||
},
|
||||
|
||||
[20] = "lwarxRR0R", [84] = "ldarxRR0R",
|
||||
|
||||
[21] = "ldxRR0R", [53] = "lduxRRR",
|
||||
[149] = "stdxRR0R", [181] = "stduxRRR",
|
||||
[341] = "lwaxRR0R", [373] = "lwauxRRR",
|
||||
|
||||
[23] = "lwzxRR0R", [55] = "lwzuxRRR",
|
||||
[87] = "lbzxRR0R", [119] = "lbzuxRRR",
|
||||
[151] = "stwxRR0R", [183] = "stwuxRRR",
|
||||
[215] = "stbxRR0R", [247] = "stbuxRRR",
|
||||
[279] = "lhzxRR0R", [311] = "lhzuxRRR",
|
||||
[343] = "lhaxRR0R", [375] = "lhauxRRR",
|
||||
[407] = "sthxRR0R", [439] = "sthuxRRR",
|
||||
|
||||
[54] = "dcbst-R0R", [86] = "dcbf-R0R",
|
||||
[150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
|
||||
[246] = "dcbtst-R0R", [278] = "dcbt-R0R",
|
||||
[310] = "eciwxRR0R", [438] = "ecowxRR0R",
|
||||
[470] = "dcbi-RR",
|
||||
|
||||
[598] = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = "sync", "lwsync", "ptesync",
|
||||
},
|
||||
[758] = "dcba-RR",
|
||||
[854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
|
||||
|
||||
[26] = "cntlzwRR~", [58] = "cntlzdRR~",
|
||||
[122] = "popcntbRR~",
|
||||
[154] = "prtywRR~", [186] = "prtydRR~",
|
||||
|
||||
[28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
|
||||
[284] = "eqvRR~R.", [316] = "xorRR~R.",
|
||||
[412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
|
||||
[508] = "cmpbRR~R",
|
||||
|
||||
[512] = "mcrxrX",
|
||||
|
||||
[532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
|
||||
|
||||
[533] = "lswxRR0R", [597] = "lswiRR0A",
|
||||
[661] = "stswxRR0R", [725] = "stswiRR0A",
|
||||
|
||||
[534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
|
||||
[790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
|
||||
|
||||
[535] = "lfsxFR0R", [567] = "lfsuxFRR",
|
||||
[599] = "lfdxFR0R", [631] = "lfduxFRR",
|
||||
[663] = "stfsxFR0R", [695] = "stfsuxFRR",
|
||||
[727] = "stfdxFR0R", [759] = "stfduxFR0R",
|
||||
[855] = "lfiwaxFR0R",
|
||||
[983] = "stfiwxFR0R",
|
||||
|
||||
[24] = "slwRR~R.",
|
||||
|
||||
[27] = "sldRR~R.", [536] = "srwRR~R.",
|
||||
[792] = "srawRR~R.", [824] = "srawiRR~A.",
|
||||
|
||||
[794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
|
||||
[922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
|
||||
|
||||
[539] = "srdRR~R.",
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
if band(x, 31) == 15 then return "iselRRRC" end
|
||||
end
|
||||
})
|
||||
|
||||
local map_ld = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "ldRRE", "lduRRE", "lwaRRE",
|
||||
}
|
||||
|
||||
local map_std = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "stdRRE", "stduRRE",
|
||||
}
|
||||
|
||||
local map_fps = {
|
||||
shift = 5, mask = 1,
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivsFFF.", false,
|
||||
"fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
|
||||
"fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
|
||||
"fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_fpd = {
|
||||
shift = 5, mask = 1,
|
||||
[0] = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
|
||||
[38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
|
||||
[8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
|
||||
[136] = "fnabsF-F.", [264] = "fabsF-F.",
|
||||
[12] = "frspF-F.",
|
||||
[14] = "fctiwF-F.", [15] = "fctiwzF-F.",
|
||||
[583] = "mffsF.", [711] = "mtfsfZF.",
|
||||
[392] = "frinF-F.", [424] = "frizF-F.",
|
||||
[456] = "fripF-F.", [488] = "frimF-F.",
|
||||
[814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
|
||||
},
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivFFF.", false,
|
||||
"fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
|
||||
"freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
|
||||
"fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_spe = {
|
||||
shift = 0, mask = 2047,
|
||||
|
||||
[512] = "evaddwRRR", [514] = "evaddiwRAR~",
|
||||
[516] = "evsubwRRR~", [518] = "evsubiwRAR~",
|
||||
[520] = "evabsRR", [521] = "evnegRR",
|
||||
[522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
|
||||
[525] = "evcntlzwRR", [526] = "evcntlswRR",
|
||||
|
||||
[527] = "brincRRR",
|
||||
|
||||
[529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
|
||||
[535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
|
||||
[537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
|
||||
|
||||
[544] = "evsrwuRRR", [545] = "evsrwsRRR",
|
||||
[546] = "evsrwiuRRA", [547] = "evsrwisRRA",
|
||||
[548] = "evslwRRR", [550] = "evslwiRRA",
|
||||
[552] = "evrlwRRR", [553] = "evsplatiRS",
|
||||
[554] = "evrlwiRRA", [555] = "evsplatfiRS",
|
||||
[556] = "evmergehiRRR", [557] = "evmergeloRRR",
|
||||
[558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
|
||||
|
||||
[560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
|
||||
[562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
|
||||
[564] = "evcmpeqYRR",
|
||||
|
||||
[632] = "evselRRR", [633] = "evselRRRW",
|
||||
[634] = "evselRRRW", [635] = "evselRRRW",
|
||||
[636] = "evselRRRW", [637] = "evselRRRW",
|
||||
[638] = "evselRRRW", [639] = "evselRRRW",
|
||||
|
||||
[640] = "evfsaddRRR", [641] = "evfssubRRR",
|
||||
[644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
|
||||
[648] = "evfsmulRRR", [649] = "evfsdivRRR",
|
||||
[652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
|
||||
[656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
|
||||
[658] = "evfscfufR-R", [659] = "evfscfsfR-R",
|
||||
[660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
|
||||
[662] = "evfsctufR-R", [663] = "evfsctsfR-R",
|
||||
[664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
|
||||
[668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
|
||||
|
||||
[704] = "efsaddRRR", [705] = "efssubRRR",
|
||||
[708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
|
||||
[712] = "efsmulRRR", [713] = "efsdivRRR",
|
||||
[716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
|
||||
[719] = "efscfdR-R",
|
||||
[720] = "efscfuiR-R", [721] = "efscfsiR-R",
|
||||
[722] = "efscfufR-R", [723] = "efscfsfR-R",
|
||||
[724] = "efsctuiR-R", [725] = "efsctsiR-R",
|
||||
[726] = "efsctufR-R", [727] = "efsctsfR-R",
|
||||
[728] = "efsctuizR-R", [730] = "efsctsizR-R",
|
||||
[732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
|
||||
|
||||
[736] = "efdaddRRR", [737] = "efdsubRRR",
|
||||
[738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
|
||||
[740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
|
||||
[744] = "efdmulRRR", [745] = "efddivRRR",
|
||||
[746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
|
||||
[748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
|
||||
[751] = "efdcfsR-R",
|
||||
[752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
|
||||
[754] = "efdcfufR-R", [755] = "efdcfsfR-R",
|
||||
[756] = "efdctuiR-R", [757] = "efdctsiR-R",
|
||||
[758] = "efdctufR-R", [759] = "efdctsfR-R",
|
||||
[760] = "efdctuizR-R", [762] = "efdctsizR-R",
|
||||
[764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
|
||||
|
||||
[768] = "evlddxRR0R", [769] = "evlddRR8",
|
||||
[770] = "evldwxRR0R", [771] = "evldwRR8",
|
||||
[772] = "evldhxRR0R", [773] = "evldhRR8",
|
||||
[776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
|
||||
[780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
|
||||
[782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
|
||||
[784] = "evlwhexRR0R", [785] = "evlwheRR4",
|
||||
[788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
|
||||
[790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
|
||||
[792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
|
||||
[796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
|
||||
|
||||
[800] = "evstddxRR0R", [801] = "evstddRR8",
|
||||
[802] = "evstdwxRR0R", [803] = "evstdwRR8",
|
||||
[804] = "evstdhxRR0R", [805] = "evstdhRR8",
|
||||
[816] = "evstwhexRR0R", [817] = "evstwheRR4",
|
||||
[820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
|
||||
[824] = "evstwwexRR0R", [825] = "evstwweRR4",
|
||||
[828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
|
||||
|
||||
[1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
|
||||
[1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
|
||||
[1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
|
||||
[1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
|
||||
[1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
|
||||
[1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
|
||||
[1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
|
||||
[1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
|
||||
[1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
|
||||
[1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
|
||||
[1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
|
||||
[1147] = "evmwsmfaRRR",
|
||||
|
||||
[1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
|
||||
[1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
|
||||
[1220] = "evmraRR",
|
||||
[1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
|
||||
[1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
|
||||
[1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
|
||||
|
||||
[1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
|
||||
[1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
|
||||
[1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
|
||||
[1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
|
||||
[1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
|
||||
[1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
|
||||
[1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
|
||||
[1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
|
||||
[1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
|
||||
[1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
|
||||
[1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
|
||||
[1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
|
||||
[1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
|
||||
[1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
|
||||
[1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
|
||||
[1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
|
||||
[1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
|
||||
[1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
|
||||
[1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
|
||||
[1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
|
||||
[1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
|
||||
[1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
|
||||
[1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
|
||||
[1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
|
||||
[1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
|
||||
[1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = false, false, "tdiARI", "twiARI",
|
||||
map_spe, false, false, "mulliRRI",
|
||||
"subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
|
||||
"addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
|
||||
"b_KBJ", "sc", "bKJ", map_crops,
|
||||
"rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
|
||||
"oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
|
||||
"andi.RR~U", "andis.RR~U", map_rld, map_ext,
|
||||
"lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
|
||||
"stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
|
||||
"lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
|
||||
"sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
|
||||
"lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
|
||||
"stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
|
||||
false, false, map_ld, map_fps,
|
||||
false, false, map_std, map_fpd,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
||||
}
|
||||
|
||||
local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
|
||||
|
||||
-- Format a condition bit.
|
||||
local function condfmt(cond)
|
||||
if cond <= 3 then
|
||||
return map_cond[band(cond, 3)]
|
||||
else
|
||||
return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
local operands = {}
|
||||
local last = nil
|
||||
local rs = 21
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = map_pri[rshift(b0, 2)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)]
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "R" then
|
||||
x = map_gpr[band(rshift(op, rs), 31)]
|
||||
rs = rs - 5
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "S" then
|
||||
x = arshift(lshift(op, 27-rs), 27)
|
||||
rs = rs - 5
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "D" or p == "E" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
if p == "E" then disp = band(disp, -4) end
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p >= "2" and p <= "8" then
|
||||
local disp = band(rshift(op, rs), 31) * p
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "H" then
|
||||
x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
|
||||
rs = rs - 5
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, rs), 31) + band(op, 0x20)
|
||||
elseif p == "C" then
|
||||
x = condfmt(band(rshift(op, rs), 31))
|
||||
rs = rs - 5
|
||||
elseif p == "B" then
|
||||
local bo = rshift(op, 21)
|
||||
local cond = band(rshift(op, 16), 31)
|
||||
local cn = ""
|
||||
rs = rs - 10
|
||||
if band(bo, 4) == 0 then
|
||||
cn = band(bo, 2) == 0 and "dnz" or "dz"
|
||||
if band(bo, 0x10) == 0 then
|
||||
cn = cn..(band(bo, 8) == 0 and "f" or "t")
|
||||
end
|
||||
if band(bo, 0x10) == 0 then x = condfmt(cond) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
elseif band(bo, 0x10) == 0 then
|
||||
cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
|
||||
if cond > 3 then x = "cr"..rshift(cond, 2) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
end
|
||||
name = gsub(name, "_", cn)
|
||||
elseif p == "J" then
|
||||
x = arshift(lshift(op, 27-rs), 29-rs)*4
|
||||
if band(op, 2) == 0 then x = ctx.addr + pos + x end
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "K" then
|
||||
if band(op, 1) ~= 0 then name = name.."l" end
|
||||
if band(op, 2) ~= 0 then name = name.."a" end
|
||||
elseif p == "X" or p == "Y" then
|
||||
x = band(rshift(op, rs+2), 7)
|
||||
if x == 0 and p == "Y" then x = nil else x = "cr"..x end
|
||||
rs = rs - 5
|
||||
elseif p == "W" then
|
||||
x = "cr"..band(op, 7)
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, rs-4), 255)
|
||||
rs = rs - 10
|
||||
elseif p == ">" then
|
||||
operands[#operands] = rshift(operands[#operands], 1)
|
||||
elseif p == "0" then
|
||||
if last == "r0" then
|
||||
operands[#operands] = nil
|
||||
if altname then name = altname end
|
||||
end
|
||||
elseif p == "L" then
|
||||
name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
|
||||
elseif p == "." then
|
||||
if band(op, 1) == 1 then name = name.."." end
|
||||
elseif p == "N" then
|
||||
if op == 0x60000000 then name = "nop"; break end
|
||||
elseif p == "~" then
|
||||
local n = #operands
|
||||
operands[n-1], operands[n] = operands[n], operands[n-1]
|
||||
elseif p == "=" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] then
|
||||
operands[n] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "%" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] and last == operands[n-2] then
|
||||
operands[n] = nil
|
||||
operands[n-1] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "-" then
|
||||
rs = rs - 5
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create_(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass_(code, addr, out)
|
||||
create_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
disass = disass_
|
||||
regname = regname_
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x64 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the 64 bit functions from the combined
|
||||
-- x86/x64 disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local require = require
|
||||
|
||||
module(...)
|
||||
|
||||
local dis_x86 = require(_PACKAGE.."dis_x86")
|
||||
|
||||
create = dis_x86.create64
|
||||
disass = dis_x86.disass64
|
||||
regname = dis_x86.regname64
|
||||
|
||||
@@ -1,836 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x86/x64 disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- Sending small code snippets to an external disassembler and mixing the
|
||||
-- output with our own stuff was too fragile. So I had to bite the bullet
|
||||
-- and write yet another x86 disassembler. Oh well ...
|
||||
--
|
||||
-- The output format is very similar to what ndisasm generates. But it has
|
||||
-- been developed independently by looking at the opcode tables from the
|
||||
-- Intel and AMD manuals. The supported instruction set is quite extensive
|
||||
-- and reflects what a current generation Intel or AMD CPU implements in
|
||||
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
|
||||
-- SSE4.1, SSE4.2, SSE4a and even privileged and hypervisor (VMX/SVM)
|
||||
-- instructions.
|
||||
--
|
||||
-- Notes:
|
||||
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
|
||||
-- * No attempt at optimization has been made -- it's fast enough for my needs.
|
||||
-- * The public API may change when more architectures are added.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local lower, rep = string.lower, string.rep
|
||||
|
||||
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
|
||||
local map_opc1_32 = {
|
||||
--0x
|
||||
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
|
||||
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
|
||||
--1x
|
||||
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
|
||||
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
|
||||
--2x
|
||||
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
|
||||
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
|
||||
--3x
|
||||
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
|
||||
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
|
||||
--4x
|
||||
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
|
||||
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
|
||||
--5x
|
||||
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
|
||||
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
|
||||
--6x
|
||||
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
|
||||
"fs:seg","gs:seg","o16:","a16",
|
||||
"pushUi","imulVrmi","pushBs","imulVrms",
|
||||
"insb","insVS","outsb","outsVS",
|
||||
--7x
|
||||
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
|
||||
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
|
||||
--8x
|
||||
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
|
||||
"testBmr","testVmr","xchgBrm","xchgVrm",
|
||||
"movBmr","movVmr","movBrm","movVrm",
|
||||
"movVmg","leaVrm","movWgm","popUm",
|
||||
--9x
|
||||
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
|
||||
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
|
||||
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
|
||||
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
|
||||
--Ax
|
||||
"movBao","movVao","movBoa","movVoa",
|
||||
"movsb","movsVS","cmpsb","cmpsVS",
|
||||
"testBai","testVai","stosb","stosVS",
|
||||
"lodsb","lodsVS","scasb","scasVS",
|
||||
--Bx
|
||||
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
|
||||
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
|
||||
--Cx
|
||||
"shift!Bmu","shift!Vmu","retBw","ret","$lesVrm","$ldsVrm","movBmi","movVmi",
|
||||
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
|
||||
--Dx
|
||||
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
|
||||
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
|
||||
--Ex
|
||||
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
|
||||
"inBau","inVau","outBua","outVua",
|
||||
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
|
||||
--Fx
|
||||
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
|
||||
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
|
||||
}
|
||||
assert(#map_opc1_32 == 255)
|
||||
|
||||
-- Map for 1st opcode byte in 64 bit mode (overrides only).
|
||||
local map_opc1_64 = setmetatable({
|
||||
[0x06]=false, [0x07]=false, [0x0e]=false,
|
||||
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
|
||||
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
|
||||
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
|
||||
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
|
||||
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
|
||||
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
|
||||
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
|
||||
[0x82]=false, [0x9a]=false, [0xc4]=false, [0xc5]=false, [0xce]=false,
|
||||
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
|
||||
}, { __index = map_opc1_32 })
|
||||
|
||||
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
|
||||
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
|
||||
local map_opc2 = {
|
||||
--0x
|
||||
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
|
||||
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
|
||||
--1x
|
||||
"movupsXrm|movssXrm|movupdXrm|movsdXrm",
|
||||
"movupsXmr|movssXmr|movupdXmr|movsdXmr",
|
||||
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
|
||||
"movlpsXmr||movlpdXmr",
|
||||
"unpcklpsXrm||unpcklpdXrm",
|
||||
"unpckhpsXrm||unpckhpdXrm",
|
||||
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
|
||||
"movhpsXmr||movhpdXmr",
|
||||
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
|
||||
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
|
||||
--2x
|
||||
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
|
||||
"movapsXrm||movapdXrm",
|
||||
"movapsXmr||movapdXmr",
|
||||
"cvtpi2psXrMm|cvtsi2ssXrVmt|cvtpi2pdXrMm|cvtsi2sdXrVmt",
|
||||
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
|
||||
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
|
||||
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
|
||||
"ucomissXrm||ucomisdXrm",
|
||||
"comissXrm||comisdXrm",
|
||||
--3x
|
||||
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
|
||||
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
|
||||
--4x
|
||||
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
|
||||
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
|
||||
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
|
||||
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
|
||||
--5x
|
||||
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
|
||||
"rsqrtpsXrm|rsqrtssXrm","rcppsXrm|rcpssXrm",
|
||||
"andpsXrm||andpdXrm","andnpsXrm||andnpdXrm",
|
||||
"orpsXrm||orpdXrm","xorpsXrm||xorpdXrm",
|
||||
"addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm",
|
||||
"cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm",
|
||||
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
|
||||
"subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm",
|
||||
"divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm",
|
||||
--6x
|
||||
"punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm",
|
||||
"pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm",
|
||||
"punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm",
|
||||
"||punpcklqdqXrm","||punpckhqdqXrm",
|
||||
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
|
||||
--7x
|
||||
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu",
|
||||
"pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu",
|
||||
"pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|",
|
||||
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
|
||||
nil,nil,
|
||||
"||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm",
|
||||
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
|
||||
--8x
|
||||
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
|
||||
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
|
||||
--9x
|
||||
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
|
||||
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
|
||||
--Ax
|
||||
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
|
||||
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
|
||||
--Bx
|
||||
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
|
||||
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
|
||||
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
|
||||
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
|
||||
--Cx
|
||||
"xaddBmr","xaddVmr",
|
||||
"cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|",
|
||||
"pinsrwPrWmu","pextrwDrPmu",
|
||||
"shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp",
|
||||
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
|
||||
--Dx
|
||||
"||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm",
|
||||
"paddqPrm","pmullwPrm",
|
||||
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
|
||||
"psubusbPrm","psubuswPrm","pminubPrm","pandPrm",
|
||||
"paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm",
|
||||
--Ex
|
||||
"pavgbPrm","psrawPrm","psradPrm","pavgwPrm",
|
||||
"pmulhuwPrm","pmulhwPrm",
|
||||
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
|
||||
"psubsbPrm","psubswPrm","pminswPrm","porPrm",
|
||||
"paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm",
|
||||
--Fx
|
||||
"|||lddquXrm","psllwPrm","pslldPrm","psllqPrm",
|
||||
"pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$",
|
||||
"psubbPrm","psubwPrm","psubdPrm","psubqPrm",
|
||||
"paddbPrm","paddwPrm","padddPrm","ud",
|
||||
}
|
||||
assert(map_opc2[255] == "ud")
|
||||
|
||||
-- Map for three-byte opcodes. Can't wait for their next invention.
|
||||
local map_opc3 = {
|
||||
["38"] = { -- [66] 0f 38 xx
|
||||
--0x
|
||||
[0]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm",
|
||||
"pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm",
|
||||
"psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm",
|
||||
nil,nil,nil,nil,
|
||||
--1x
|
||||
"||pblendvbXrma",nil,nil,nil,
|
||||
"||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm",
|
||||
nil,nil,nil,nil,
|
||||
"pabsbPrm","pabswPrm","pabsdPrm",nil,
|
||||
--2x
|
||||
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
|
||||
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
|
||||
"||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm",
|
||||
nil,nil,nil,nil,
|
||||
--3x
|
||||
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
|
||||
"||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm",
|
||||
"||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm",
|
||||
"||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm",
|
||||
--4x
|
||||
"||pmulddXrm","||phminposuwXrm",
|
||||
--Fx
|
||||
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
|
||||
},
|
||||
|
||||
["3a"] = { -- [66] 0f 3a xx
|
||||
--0x
|
||||
[0x00]=nil,nil,nil,nil,nil,nil,nil,nil,
|
||||
"||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu",
|
||||
"||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu",
|
||||
--1x
|
||||
nil,nil,nil,nil,
|
||||
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
|
||||
nil,nil,nil,nil,nil,nil,nil,nil,
|
||||
--2x
|
||||
"||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil,
|
||||
--4x
|
||||
[0x40] = "||dppsXrmu",
|
||||
[0x41] = "||dppdXrmu",
|
||||
[0x42] = "||mpsadbwXrmu",
|
||||
--6x
|
||||
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
|
||||
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
|
||||
},
|
||||
}
|
||||
|
||||
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
|
||||
local map_opcvm = {
|
||||
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
|
||||
[0xc8]="monitor",[0xc9]="mwait",
|
||||
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
|
||||
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
|
||||
[0xf8]="swapgs",[0xf9]="rdtscp",
|
||||
}
|
||||
|
||||
-- Map for FP opcodes. And you thought stack machines are simple?
|
||||
local map_opcfp = {
|
||||
-- D8-DF 00-BF: opcodes with a memory operand.
|
||||
-- D8
|
||||
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
|
||||
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
|
||||
-- DA
|
||||
"fiaddDm","fimulDm","ficomDm","ficompDm",
|
||||
"fisubDm","fisubrDm","fidivDm","fidivrDm",
|
||||
-- DB
|
||||
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
|
||||
-- DC
|
||||
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
|
||||
-- DD
|
||||
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
|
||||
-- DE
|
||||
"fiaddWm","fimulWm","ficomWm","ficompWm",
|
||||
"fisubWm","fisubrWm","fidivWm","fidivrWm",
|
||||
-- DF
|
||||
"fildWm","fisttpWm","fistWm","fistpWm",
|
||||
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
|
||||
-- xx C0-FF: opcodes with a pseudo-register operand.
|
||||
-- D8
|
||||
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
|
||||
-- D9
|
||||
"fldFf","fxchFf",{"fnop"},nil,
|
||||
{"fchs","fabs",nil,nil,"ftst","fxam"},
|
||||
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
|
||||
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
|
||||
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
|
||||
-- DA
|
||||
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
|
||||
-- DB
|
||||
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
|
||||
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
|
||||
-- DC
|
||||
"fadd toFf","fmul toFf",nil,nil,
|
||||
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
|
||||
-- DD
|
||||
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
|
||||
-- DE
|
||||
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
|
||||
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
|
||||
-- DF
|
||||
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
|
||||
}
|
||||
assert(map_opcfp[126] == "fcomipFf")
|
||||
|
||||
-- Map for opcode groups. The subkey is sp from the ModRM byte.
|
||||
local map_opcgroup = {
|
||||
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
|
||||
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
|
||||
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
||||
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
||||
incb = { "inc", "dec" },
|
||||
incd = { "inc", "dec", "callUmp", "$call farDmp",
|
||||
"jmpUmp", "$jmp farDmp", "pushUm" },
|
||||
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
|
||||
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
|
||||
"smsw", nil, "lmsw", "vm*$invlpg" },
|
||||
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
|
||||
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
|
||||
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
|
||||
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
|
||||
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
|
||||
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
|
||||
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
|
||||
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
|
||||
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
|
||||
prefetch = { "prefetch", "prefetchw" },
|
||||
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Maps for register names.
|
||||
local map_regs = {
|
||||
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
|
||||
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
|
||||
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
||||
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
|
||||
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
|
||||
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
|
||||
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
||||
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
|
||||
}
|
||||
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
|
||||
|
||||
-- Maps for size names.
|
||||
local map_sz2n = {
|
||||
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16,
|
||||
}
|
||||
local map_sz2prefix = {
|
||||
B = "byte", W = "word", D = "dword",
|
||||
Q = "qword",
|
||||
M = "qword", X = "xword",
|
||||
F = "dword", G = "qword", -- No need for sizes/register names for these two.
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local code, pos, hex = ctx.code, ctx.pos, ""
|
||||
local hmax = ctx.hexdump
|
||||
if hmax > 0 then
|
||||
for i=ctx.start,pos-1 do
|
||||
hex = hex..format("%02X", byte(code, i, i))
|
||||
end
|
||||
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
|
||||
else hex = hex..rep(" ", hmax-#hex+2) end
|
||||
end
|
||||
if operands then text = text.." "..operands end
|
||||
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
|
||||
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
|
||||
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
|
||||
if ctx.rex then
|
||||
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
|
||||
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")
|
||||
if t ~= "" then text = "rex."..t.." "..text end
|
||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
||||
ctx.rex = false
|
||||
end
|
||||
if ctx.seg then
|
||||
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
|
||||
if n == 0 then text = ctx.seg.." "..text else text = text2 end
|
||||
ctx.seg = false
|
||||
end
|
||||
if ctx.lock then text = "lock "..text; ctx.lock = false end
|
||||
local imm = ctx.imm
|
||||
if imm then
|
||||
local sym = ctx.symtab[imm]
|
||||
if sym then text = text.."\t->"..sym end
|
||||
end
|
||||
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
|
||||
ctx.mrm = false
|
||||
ctx.start = pos
|
||||
ctx.imm = nil
|
||||
end
|
||||
|
||||
-- Clear all prefix flags.
|
||||
local function clearprefixes(ctx)
|
||||
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
|
||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
||||
ctx.rex = false; ctx.a32 = false
|
||||
end
|
||||
|
||||
-- Fallback for incomplete opcodes at the end.
|
||||
local function incomplete(ctx)
|
||||
ctx.pos = ctx.stop+1
|
||||
clearprefixes(ctx)
|
||||
return putop(ctx, "(incomplete)")
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
clearprefixes(ctx)
|
||||
return putop(ctx, "(unknown)")
|
||||
end
|
||||
|
||||
-- Return an immediate of the specified size.
|
||||
local function getimm(ctx, pos, n)
|
||||
if pos+n-1 > ctx.stop then return incomplete(ctx) end
|
||||
local code = ctx.code
|
||||
if n == 1 then
|
||||
local b1 = byte(code, pos, pos)
|
||||
return b1
|
||||
elseif n == 2 then
|
||||
local b1, b2 = byte(code, pos, pos+1)
|
||||
return b1+b2*256
|
||||
else
|
||||
local b1, b2, b3, b4 = byte(code, pos, pos+3)
|
||||
local imm = b1+b2*256+b3*65536+b4*16777216
|
||||
ctx.imm = imm
|
||||
return imm
|
||||
end
|
||||
end
|
||||
|
||||
-- Process pattern string and generate the operands.
|
||||
local function putpat(ctx, name, pat)
|
||||
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
|
||||
local code, pos, stop = ctx.code, ctx.pos, ctx.stop
|
||||
|
||||
-- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "V" or p == "U" then
|
||||
if ctx.rexw then sz = "Q"; ctx.rexw = false
|
||||
elseif ctx.o16 then sz = "W"; ctx.o16 = false
|
||||
elseif p == "U" and ctx.x64 then sz = "Q"
|
||||
else sz = "D" end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "T" then
|
||||
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
|
||||
regs = map_regs[sz]
|
||||
elseif p == "B" then
|
||||
sz = "B"
|
||||
regs = ctx.rex and map_regs.B64 or map_regs.B
|
||||
elseif match(p, "[WDQMXFG]") then
|
||||
sz = p
|
||||
regs = map_regs[sz]
|
||||
elseif p == "P" then
|
||||
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
|
||||
regs = map_regs[sz]
|
||||
elseif p == "S" then
|
||||
name = name..lower(sz)
|
||||
elseif p == "s" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = imm <= 127 and format("+0x%02x", imm)
|
||||
or format("-0x%02x", 256-imm)
|
||||
pos = pos+1
|
||||
elseif p == "u" then
|
||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
||||
x = format("0x%02x", imm)
|
||||
pos = pos+1
|
||||
elseif p == "w" then
|
||||
local imm = getimm(ctx, pos, 2); if not imm then return end
|
||||
x = format("0x%x", imm)
|
||||
pos = pos+2
|
||||
elseif p == "o" then -- [offset]
|
||||
if ctx.x64 then
|
||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
||||
x = format("[0x%08x%08x]", imm2, imm1)
|
||||
pos = pos+8
|
||||
else
|
||||
local imm = getimm(ctx, pos, 4); if not imm then return end
|
||||
x = format("[0x%08x]", imm)
|
||||
pos = pos+4
|
||||
end
|
||||
elseif p == "i" or p == "I" then
|
||||
local n = map_sz2n[sz]
|
||||
if n == 8 and ctx.x64 and p == "I" then
|
||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
||||
x = format("0x%08x%08x", imm2, imm1)
|
||||
else
|
||||
if n == 8 then n = 4 end
|
||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
||||
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
|
||||
imm = (0xffffffff+1)-imm
|
||||
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
|
||||
else
|
||||
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
|
||||
end
|
||||
end
|
||||
pos = pos+n
|
||||
elseif p == "j" then
|
||||
local n = map_sz2n[sz]
|
||||
if n == 8 then n = 4 end
|
||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
||||
if sz == "B" and imm > 127 then imm = imm-256
|
||||
elseif imm > 2147483647 then imm = imm-4294967296 end
|
||||
pos = pos+n
|
||||
imm = imm + pos + ctx.addr
|
||||
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
|
||||
ctx.imm = imm
|
||||
if sz == "W" then
|
||||
x = format("word 0x%04x", imm%65536)
|
||||
elseif ctx.x64 then
|
||||
local lo = imm % 0x1000000
|
||||
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
|
||||
else
|
||||
x = format("0x%08x", imm)
|
||||
end
|
||||
elseif p == "R" then
|
||||
local r = byte(code, pos-1, pos-1)%8
|
||||
if ctx.rexb then r = r + 8; ctx.rexb = false end
|
||||
x = regs[r+1]
|
||||
elseif p == "a" then x = regs[1]
|
||||
elseif p == "c" then x = "cl"
|
||||
elseif p == "d" then x = "dx"
|
||||
elseif p == "1" then x = "1"
|
||||
else
|
||||
if not mode then
|
||||
mode = ctx.mrm
|
||||
if not mode then
|
||||
if pos > stop then return incomplete(ctx) end
|
||||
mode = byte(code, pos, pos)
|
||||
pos = pos+1
|
||||
end
|
||||
rm = mode%8; mode = (mode-rm)/8
|
||||
sp = mode%8; mode = (mode-sp)/8
|
||||
sdisp = ""
|
||||
if mode < 3 then
|
||||
if rm == 4 then
|
||||
if pos > stop then return incomplete(ctx) end
|
||||
sc = byte(code, pos, pos)
|
||||
pos = pos+1
|
||||
rm = sc%8; sc = (sc-rm)/8
|
||||
rx = sc%8; sc = (sc-rx)/8
|
||||
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
|
||||
if rx == 4 then rx = nil end
|
||||
end
|
||||
if mode > 0 or rm == 5 then
|
||||
local dsz = mode
|
||||
if dsz ~= 1 then dsz = 4 end
|
||||
local disp = getimm(ctx, pos, dsz); if not disp then return end
|
||||
if mode == 0 then rm = nil end
|
||||
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
|
||||
if dsz == 1 and disp > 127 then
|
||||
sdisp = format("-0x%x", 256-disp)
|
||||
elseif disp >= 0 and disp <= 0x7fffffff then
|
||||
sdisp = format("+0x%x", disp)
|
||||
else
|
||||
sdisp = format("-0x%x", (0xffffffff+1)-disp)
|
||||
end
|
||||
else
|
||||
sdisp = format(ctx.x64 and not ctx.a32 and
|
||||
not (disp >= 0 and disp <= 0x7fffffff)
|
||||
and "0xffffffff%08x" or "0x%08x", disp)
|
||||
end
|
||||
pos = pos+dsz
|
||||
end
|
||||
end
|
||||
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
|
||||
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
|
||||
end
|
||||
if p == "m" then
|
||||
if mode == 3 then x = regs[rm+1]
|
||||
else
|
||||
local aregs = ctx.a32 and map_regs.D or ctx.aregs
|
||||
local srm, srx = "", ""
|
||||
if rm then srm = aregs[rm+1]
|
||||
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
|
||||
ctx.a32 = false
|
||||
if rx then
|
||||
if rm then srm = srm.."+" end
|
||||
srx = aregs[rx+1]
|
||||
if sc > 0 then srx = srx.."*"..(2^sc) end
|
||||
end
|
||||
x = format("[%s%s%s]", srm, srx, sdisp)
|
||||
end
|
||||
if mode < 3 and
|
||||
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
|
||||
x = map_sz2prefix[sz].." "..x
|
||||
end
|
||||
elseif p == "r" then x = regs[sp+1]
|
||||
elseif p == "g" then x = map_segregs[sp+1]
|
||||
elseif p == "p" then -- Suppress prefix.
|
||||
elseif p == "f" then x = "st"..rm
|
||||
elseif p == "x" then
|
||||
if sp == 0 and ctx.lock and not ctx.x64 then
|
||||
x = "CR8"; ctx.lock = false
|
||||
else
|
||||
x = "CR"..sp
|
||||
end
|
||||
elseif p == "y" then x = "DR"..sp
|
||||
elseif p == "z" then x = "TR"..sp
|
||||
elseif p == "t" then
|
||||
else
|
||||
error("bad pattern `"..pat.."'")
|
||||
end
|
||||
end
|
||||
if x then operands = operands and operands..", "..x or x end
|
||||
end
|
||||
ctx.pos = pos
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
-- Forward declaration.
|
||||
local map_act
|
||||
|
||||
-- Fetch and cache MRM byte.
|
||||
local function getmrm(ctx)
|
||||
local mrm = ctx.mrm
|
||||
if not mrm then
|
||||
local pos = ctx.pos
|
||||
if pos > ctx.stop then return nil end
|
||||
mrm = byte(ctx.code, pos, pos)
|
||||
ctx.pos = pos+1
|
||||
ctx.mrm = mrm
|
||||
end
|
||||
return mrm
|
||||
end
|
||||
|
||||
-- Dispatch to handler depending on pattern.
|
||||
local function dispatch(ctx, opat, patgrp)
|
||||
if not opat then return unknown(ctx) end
|
||||
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
|
||||
local p
|
||||
if ctx.rep then
|
||||
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
|
||||
ctx.rep = false
|
||||
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
|
||||
else p = "^[^%|]*" end
|
||||
opat = match(opat, p)
|
||||
if not opat then return unknown(ctx) end
|
||||
-- ctx.rep = false; ctx.o16 = false
|
||||
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
|
||||
--XXX remove in branches?
|
||||
end
|
||||
if match(opat, "%$") then -- reg$mem variants.
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
|
||||
if opat == "" then return unknown(ctx) end
|
||||
end
|
||||
if opat == "" then return unknown(ctx) end
|
||||
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
|
||||
if pat == "" and patgrp then pat = patgrp end
|
||||
return map_act[sub(pat, 1, 1)](ctx, name, pat)
|
||||
end
|
||||
|
||||
-- Get a pattern from an opcode map and dispatch to handler.
|
||||
local function dispatchmap(ctx, opcmap)
|
||||
local pos = ctx.pos
|
||||
local opat = opcmap[byte(ctx.code, pos, pos)]
|
||||
pos = pos + 1
|
||||
ctx.pos = pos
|
||||
return dispatch(ctx, opat)
|
||||
end
|
||||
|
||||
-- Map for action codes. The key is the first char after the name.
|
||||
map_act = {
|
||||
-- Simple opcodes without operands.
|
||||
[""] = function(ctx, name, pat)
|
||||
return putop(ctx, name)
|
||||
end,
|
||||
|
||||
-- Operand size chars fall right through.
|
||||
B = putpat, W = putpat, D = putpat, Q = putpat,
|
||||
V = putpat, U = putpat, T = putpat,
|
||||
M = putpat, X = putpat, P = putpat,
|
||||
F = putpat, G = putpat,
|
||||
|
||||
-- Collect prefixes.
|
||||
[":"] = function(ctx, name, pat)
|
||||
ctx[pat == ":" and name or sub(pat, 2)] = name
|
||||
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
|
||||
end,
|
||||
|
||||
-- Chain to special handler specified by name.
|
||||
["*"] = function(ctx, name, pat)
|
||||
return map_act[name](ctx, name, sub(pat, 2))
|
||||
end,
|
||||
|
||||
-- Use named subtable for opcode group.
|
||||
["!"] = function(ctx, name, pat)
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
|
||||
end,
|
||||
|
||||
-- o16,o32[,o64] variants.
|
||||
sz = function(ctx, name, pat)
|
||||
if ctx.o16 then ctx.o16 = false
|
||||
else
|
||||
pat = match(pat, ",(.*)")
|
||||
if ctx.rexw then
|
||||
local p = match(pat, ",(.*)")
|
||||
if p then pat = p; ctx.rexw = false end
|
||||
end
|
||||
end
|
||||
pat = match(pat, "^[^,]*")
|
||||
return dispatch(ctx, pat)
|
||||
end,
|
||||
|
||||
-- Two-byte opcode dispatch.
|
||||
opc2 = function(ctx, name, pat)
|
||||
return dispatchmap(ctx, map_opc2)
|
||||
end,
|
||||
|
||||
-- Three-byte opcode dispatch.
|
||||
opc3 = function(ctx, name, pat)
|
||||
return dispatchmap(ctx, map_opc3[pat])
|
||||
end,
|
||||
|
||||
-- VMX/SVM dispatch.
|
||||
vm = function(ctx, name, pat)
|
||||
return dispatch(ctx, map_opcvm[ctx.mrm])
|
||||
end,
|
||||
|
||||
-- Floating point opcode dispatch.
|
||||
fp = function(ctx, name, pat)
|
||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
||||
local rm = mrm%8
|
||||
local idx = pat*8 + ((mrm-rm)/8)%8
|
||||
if mrm >= 192 then idx = idx + 64 end
|
||||
local opat = map_opcfp[idx]
|
||||
if type(opat) == "table" then opat = opat[rm+1] end
|
||||
return dispatch(ctx, opat)
|
||||
end,
|
||||
|
||||
-- REX prefix.
|
||||
rex = function(ctx, name, pat)
|
||||
if ctx.rex then return unknown(ctx) end -- Only 1 REX prefix allowed.
|
||||
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
|
||||
ctx.rex = true
|
||||
end,
|
||||
|
||||
-- Special case for nop with REX prefix.
|
||||
nop = function(ctx, name, pat)
|
||||
return dispatch(ctx, ctx.rex and pat or "nop")
|
||||
end,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
ofs = ofs + 1
|
||||
ctx.start = ofs
|
||||
ctx.pos = ofs
|
||||
ctx.stop = stop
|
||||
ctx.imm = nil
|
||||
ctx.mrm = false
|
||||
clearprefixes(ctx)
|
||||
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
|
||||
if ctx.pos ~= ctx.start then incomplete(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create_(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = (addr or 0) - 1
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 16
|
||||
ctx.x64 = false
|
||||
ctx.map1 = map_opc1_32
|
||||
ctx.aregs = map_regs.D
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create64_(code, addr, out)
|
||||
local ctx = create_(code, addr, out)
|
||||
ctx.x64 = true
|
||||
ctx.map1 = map_opc1_64
|
||||
ctx.aregs = map_regs.Q
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass_(code, addr, out)
|
||||
create_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass64_(code, addr, out)
|
||||
create64_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 8 then return map_regs.D[r+1] end
|
||||
return map_regs.X[r-7]
|
||||
end
|
||||
|
||||
local function regname64_(r)
|
||||
if r < 16 then return map_regs.Q[r+1] end
|
||||
return map_regs.X[r-15]
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
create64 = create64_
|
||||
disass = disass_
|
||||
disass64 = disass64_
|
||||
regname = regname_
|
||||
regname64 = regname64_
|
||||
|
||||
@@ -1,700 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT compiler dump module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module can be used to debug the JIT compiler itself. It dumps the
|
||||
-- code representations and structures used in various compiler stages.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
|
||||
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
|
||||
-- luajit -jdump=is myapp.lua | less -R
|
||||
-- luajit -jdump=-b myapp.lua
|
||||
-- luajit -jdump=+aH,myapp.html myapp.lua
|
||||
-- luajit -jdump=ixT,myapp.dump myapp.lua
|
||||
--
|
||||
-- The first argument specifies the dump mode. The second argument gives
|
||||
-- the output file name. Default output is to stdout, unless the environment
|
||||
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- Different features can be turned on or off with the dump mode. If the
|
||||
-- mode starts with a '+', the following features are added to the default
|
||||
-- set of features; a '-' removes them. Otherwise the features are replaced.
|
||||
--
|
||||
-- The following dump features are available (* marks the default):
|
||||
--
|
||||
-- * t Print a line for each started, ended or aborted trace (see also -jv).
|
||||
-- * b Dump the traced bytecode.
|
||||
-- * i Dump the IR (intermediate representation).
|
||||
-- r Augment the IR with register/stack slots.
|
||||
-- s Dump the snapshot map.
|
||||
-- * m Dump the generated machine code.
|
||||
-- x Print each taken trace exit.
|
||||
-- X Print each taken trace exit and the contents of all registers.
|
||||
--
|
||||
-- The output format can be set with the following characters:
|
||||
--
|
||||
-- T Plain text output.
|
||||
-- A ANSI-colored text output
|
||||
-- H Colorized HTML + CSS output.
|
||||
--
|
||||
-- The default output format is plain text. It's set to ANSI-colored text
|
||||
-- if the COLORTERM variable is set. Note: this is independent of any output
|
||||
-- redirection, which is actually considered a feature.
|
||||
--
|
||||
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
|
||||
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20001, "LuaJIT core/library version mismatch")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
|
||||
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
|
||||
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
|
||||
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
|
||||
local bit = require("bit")
|
||||
local band, shl, shr = bit.band, bit.lshift, bit.rshift
|
||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
||||
local byte, char, rep = string.byte, string.char, string.rep
|
||||
local type, tostring = type, tostring
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
-- Load other modules on-demand.
|
||||
local bcline, disass
|
||||
|
||||
-- Active flag, output file handle and dump mode.
|
||||
local active, out, dumpmode
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local symtabmt = { __index = false }
|
||||
local symtab = {}
|
||||
local nexitsym = 0
|
||||
|
||||
-- Fill nested symbol table with per-trace exit stub addresses.
|
||||
local function fillsymtab_tr(tr, nexit)
|
||||
local t = {}
|
||||
symtabmt.__index = t
|
||||
if jit.arch == "mips" or jit.arch == "mipsel" then
|
||||
t[traceexitstub(tr, 0)] = "exit"
|
||||
return
|
||||
end
|
||||
for i=0,nexit-1 do
|
||||
local addr = traceexitstub(tr, i)
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
local addr = traceexitstub(tr, nexit)
|
||||
if addr then t[addr] = "stack_check" end
|
||||
end
|
||||
|
||||
-- Fill symbol table with trace exit stub addresses.
|
||||
local function fillsymtab(tr, nexit)
|
||||
local t = symtab
|
||||
if nexitsym == 0 then
|
||||
local ircall = vmdef.ircall
|
||||
for i=0,#ircall do
|
||||
local addr = ircalladdr(i)
|
||||
if addr ~= 0 then t[addr] = ircall[i] end
|
||||
end
|
||||
end
|
||||
if nexitsym == 1000000 then -- Per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
elseif nexit > nexitsym then -- Shared exit stubs.
|
||||
for i=nexitsym,nexit-1 do
|
||||
local addr = traceexitstub(i)
|
||||
if addr == nil then -- Fall back to per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
setmetatable(symtab, symtabmt)
|
||||
nexit = 1000000
|
||||
break
|
||||
end
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
nexitsym = nexit
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function dumpwrite(s)
|
||||
out:write(s)
|
||||
end
|
||||
|
||||
-- Disassemble machine code.
|
||||
local function dump_mcode(tr)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local mcode, addr, loop = tracemc(tr)
|
||||
if not mcode then return end
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
|
||||
local ctx = disass.create(mcode, addr, dumpwrite)
|
||||
ctx.hexdump = 0
|
||||
ctx.symtab = fillsymtab(tr, info.nexit)
|
||||
if loop ~= 0 then
|
||||
symtab[addr+loop] = "LOOP"
|
||||
ctx:disass(0, loop)
|
||||
out:write("->LOOP:\n")
|
||||
ctx:disass(loop, #mcode-loop)
|
||||
symtab[addr+loop] = nil
|
||||
else
|
||||
ctx:disass(0, #mcode)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local irtype_text = {
|
||||
[0] = "nil",
|
||||
"fal",
|
||||
"tru",
|
||||
"lud",
|
||||
"str",
|
||||
"p32",
|
||||
"thr",
|
||||
"pro",
|
||||
"fun",
|
||||
"p64",
|
||||
"cdt",
|
||||
"tab",
|
||||
"udt",
|
||||
"flt",
|
||||
"num",
|
||||
"i8 ",
|
||||
"u8 ",
|
||||
"i16",
|
||||
"u16",
|
||||
"int",
|
||||
"u32",
|
||||
"i64",
|
||||
"u64",
|
||||
"sfp",
|
||||
}
|
||||
|
||||
local colortype_ansi = {
|
||||
[0] = "%s",
|
||||
"%s",
|
||||
"%s",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[32m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[33m%s\027[m",
|
||||
"\027[31m%s\027[m",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
}
|
||||
|
||||
local function colorize_text(s, t)
|
||||
return s
|
||||
end
|
||||
|
||||
local function colorize_ansi(s, t)
|
||||
return format(colortype_ansi[t], s)
|
||||
end
|
||||
|
||||
local irtype_ansi = setmetatable({},
|
||||
{ __index = function(tab, t)
|
||||
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
|
||||
|
||||
local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", }
|
||||
|
||||
local function colorize_html(s, t)
|
||||
s = gsub(s, "[<>&]", html_escape)
|
||||
return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
|
||||
end
|
||||
|
||||
local irtype_html = setmetatable({},
|
||||
{ __index = function(tab, t)
|
||||
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
|
||||
|
||||
local header_html = [[
|
||||
<style type="text/css">
|
||||
background { background: #ffffff; color: #000000; }
|
||||
pre.ljdump {
|
||||
font-size: 10pt;
|
||||
background: #f0f4ff;
|
||||
color: #000000;
|
||||
border: 1px solid #bfcfff;
|
||||
padding: 0.5em;
|
||||
margin-left: 2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
span.irt_str { color: #00a000; }
|
||||
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
|
||||
span.irt_tab { color: #c00000; }
|
||||
span.irt_udt, span.irt_lud { color: #00c0c0; }
|
||||
span.irt_num { color: #4040c0; }
|
||||
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
|
||||
</style>
|
||||
]]
|
||||
|
||||
local colorize, irtype
|
||||
|
||||
-- Lookup tables to convert some literals into names.
|
||||
local litname = {
|
||||
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = ""
|
||||
if band(mode, 1) ~= 0 then s = s.."P" end
|
||||
if band(mode, 2) ~= 0 then s = s.."F" end
|
||||
if band(mode, 4) ~= 0 then s = s.."T" end
|
||||
if band(mode, 8) ~= 0 then s = s.."C" end
|
||||
if band(mode, 16) ~= 0 then s = s.."R" end
|
||||
if band(mode, 32) ~= 0 then s = s.."I" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
|
||||
["CONV "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = irtype[band(mode, 31)]
|
||||
s = irtype[band(shr(mode, 5), 31)].."."..s
|
||||
if band(mode, 0x400) ~= 0 then s = s.." trunc"
|
||||
elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
|
||||
local c = shr(mode, 14)
|
||||
if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["FLOAD "] = vmdef.irfield,
|
||||
["FREF "] = vmdef.irfield,
|
||||
["FPMATH"] = vmdef.irfpm,
|
||||
}
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
local function formatk(tr, idx)
|
||||
local k, t, slot = tracek(tr, idx)
|
||||
local tn = type(k)
|
||||
local s
|
||||
if tn == "number" then
|
||||
if k == 2^52+2^51 then
|
||||
s = "bias"
|
||||
else
|
||||
s = format("%+.14g", k)
|
||||
end
|
||||
elseif tn == "string" then
|
||||
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
|
||||
elseif tn == "function" then
|
||||
s = fmtfunc(k)
|
||||
elseif tn == "table" then
|
||||
s = format("{%p}", k)
|
||||
elseif tn == "userdata" then
|
||||
if t == 12 then
|
||||
s = format("userdata:%p", k)
|
||||
else
|
||||
s = format("[%p]", k)
|
||||
if s == "[0x00000000]" then s = "NULL" end
|
||||
end
|
||||
elseif t == 21 then -- int64_t
|
||||
s = sub(tostring(k), 1, -3)
|
||||
if sub(s, 1, 1) ~= "-" then s = "+"..s end
|
||||
else
|
||||
s = tostring(k) -- For primitives.
|
||||
end
|
||||
s = colorize(format("%-4s", s), t)
|
||||
if slot then
|
||||
s = format("%s @%d", s, slot)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function printsnap(tr, snap)
|
||||
local n = 2
|
||||
for s=0,snap[1]-1 do
|
||||
local sn = snap[n]
|
||||
if shr(sn, 24) == s then
|
||||
n = n + 1
|
||||
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
|
||||
if ref < 0 then
|
||||
out:write(formatk(tr, ref))
|
||||
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
|
||||
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ref)
|
||||
out:write(colorize(format("%04d", ref), band(ot, 31)))
|
||||
end
|
||||
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
|
||||
else
|
||||
out:write("---- ")
|
||||
end
|
||||
end
|
||||
out:write("]\n")
|
||||
end
|
||||
|
||||
-- Dump snapshots (not interleaved with IR).
|
||||
local function dump_snap(tr)
|
||||
out:write("---- TRACE ", tr, " snapshots\n")
|
||||
for i=0,1000000000 do
|
||||
local snap = tracesnap(tr, i)
|
||||
if not snap then break end
|
||||
out:write(format("#%-3d %04d [ ", i, snap[0]))
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
-- Return a register name or stack slot for a rid/sp location.
|
||||
local function ridsp_name(ridsp, ins)
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
|
||||
if rid == 253 or rid == 254 then
|
||||
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
|
||||
end
|
||||
if ridsp > 255 then return format("[%x]", slot*4) end
|
||||
if rid < 128 then return disass.regname(rid) end
|
||||
return ""
|
||||
end
|
||||
|
||||
-- Dump CALL* function ref and return optional ctype.
|
||||
local function dumpcallfunc(tr, ins)
|
||||
local ctype
|
||||
if ins > 0 then
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
|
||||
ins = op1
|
||||
ctype = formatk(tr, op2)
|
||||
end
|
||||
end
|
||||
if ins < 0 then
|
||||
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
|
||||
else
|
||||
out:write(format("%04d (", ins))
|
||||
end
|
||||
return ctype
|
||||
end
|
||||
|
||||
-- Recursively gather CALL* args and dump them.
|
||||
local function dumpcallargs(tr, ins)
|
||||
if ins < 0 then
|
||||
out:write(formatk(tr, ins))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
local oidx = 6*shr(ot, 8)
|
||||
local op = sub(vmdef.irnames, oidx+1, oidx+6)
|
||||
if op == "CARG " then
|
||||
dumpcallargs(tr, op1)
|
||||
if op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(" ", format("%04d", op2))
|
||||
end
|
||||
else
|
||||
out:write(format("%04d", ins))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Dump IR and interleaved snapshots.
|
||||
local function dump_ir(tr, dumpsnap, dumpreg)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local nins = info.nins
|
||||
out:write("---- TRACE ", tr, " IR\n")
|
||||
local irnames = vmdef.irnames
|
||||
local snapref = 65536
|
||||
local snap, snapno
|
||||
if dumpsnap then
|
||||
snap = tracesnap(tr, 0)
|
||||
snapref = snap[0]
|
||||
snapno = 0
|
||||
end
|
||||
for ins=1,nins do
|
||||
if ins >= snapref then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
snapno = snapno + 1
|
||||
snap = tracesnap(tr, snapno)
|
||||
snapref = snap and snap[0] or 65536
|
||||
end
|
||||
local m, ot, op1, op2, ridsp = traceir(tr, ins)
|
||||
local oidx, t = 6*shr(ot, 8), band(ot, 31)
|
||||
local op = sub(irnames, oidx+1, oidx+6)
|
||||
if op == "LOOP " then
|
||||
if dumpreg then
|
||||
out:write(format("%04d ------------ LOOP ------------\n", ins))
|
||||
else
|
||||
out:write(format("%04d ------ LOOP ------------\n", ins))
|
||||
end
|
||||
elseif op ~= "NOP " and op ~= "CARG " and
|
||||
(dumpreg or op ~= "RENAME") then
|
||||
local rid = band(ridsp, 255)
|
||||
if dumpreg then
|
||||
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
|
||||
else
|
||||
out:write(format("%04d ", ins))
|
||||
end
|
||||
out:write(format("%s%s %s %s ",
|
||||
(rid == 254 or rid == 253) and "}" or
|
||||
(band(ot, 128) == 0 and " " or ">"),
|
||||
band(ot, 64) == 0 and " " or "+",
|
||||
irtype[t], op))
|
||||
local m1, m2 = band(m, 3), band(m, 3*4)
|
||||
if sub(op, 1, 4) == "CALL" then
|
||||
local ctype
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
out:write(format("%-10s (", vmdef.ircall[op2]))
|
||||
else
|
||||
ctype = dumpcallfunc(tr, op2)
|
||||
end
|
||||
if op1 ~= -1 then dumpcallargs(tr, op1) end
|
||||
out:write(")")
|
||||
if ctype then out:write(" ctype ", ctype) end
|
||||
elseif op == "CNEW " and op2 == -1 then
|
||||
out:write(formatk(tr, op1))
|
||||
elseif m1 ~= 3 then -- op1 != IRMnone
|
||||
if op1 < 0 then
|
||||
out:write(formatk(tr, op1))
|
||||
else
|
||||
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
|
||||
end
|
||||
if m2 ~= 3*4 then -- op2 != IRMnone
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
local litn = litname[op]
|
||||
if litn and litn[op2] then
|
||||
out:write(" ", litn[op2])
|
||||
elseif op == "UREFO " or op == "UREFC " then
|
||||
out:write(format(" #%-3d", shr(op2, 8)))
|
||||
else
|
||||
out:write(format(" #%-3d", op2))
|
||||
end
|
||||
elseif op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(format(" %04d", op2))
|
||||
end
|
||||
end
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
end
|
||||
if snap then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local recprefix = ""
|
||||
local recdepth = 0
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
err = format(vmdef.traceerr[err], info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "stop" or (what == "abort" and dumpmode.a) then
|
||||
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
|
||||
elseif dumpmode.s then dump_snap(tr) end
|
||||
if dumpmode.m then dump_mcode(tr) end
|
||||
end
|
||||
if what == "start" then
|
||||
if dumpmode.H then out:write('<pre class="ljdump">\n') end
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
if otr then out:write(" ", otr, "/", oex) end
|
||||
out:write(" ", fmtfunc(func, pc), "\n")
|
||||
recprefix = ""
|
||||
elseif what == "stop" or what == "abort" then
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
recprefix = nil
|
||||
if what == "abort" then
|
||||
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
|
||||
else
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if link == tr or link == 0 then
|
||||
out:write(" -> ", ltype, "\n")
|
||||
elseif ltype == "root" then
|
||||
out:write(" -> ", link, "\n")
|
||||
else
|
||||
out:write(" -> ", link, " ", ltype, "\n")
|
||||
end
|
||||
end
|
||||
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
|
||||
else
|
||||
out:write("---- TRACE ", what, "\n\n")
|
||||
end
|
||||
out:flush()
|
||||
end
|
||||
|
||||
-- Dump recorded bytecode.
|
||||
local function dump_record(tr, func, pc, depth, callee)
|
||||
if depth ~= recdepth then
|
||||
recdepth = depth
|
||||
recprefix = rep(" .", depth)
|
||||
end
|
||||
local line
|
||||
if pc >= 0 then
|
||||
line = bcline(func, pc, recprefix)
|
||||
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
|
||||
else
|
||||
line = "0000 "..recprefix.." FUNCC \n"
|
||||
callee = func
|
||||
end
|
||||
if pc <= 0 then
|
||||
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
|
||||
else
|
||||
out:write(line)
|
||||
end
|
||||
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
|
||||
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump taken trace exits.
|
||||
local function dump_texit(tr, ex, ngpr, nfpr, ...)
|
||||
out:write("---- TRACE ", tr, " exit ", ex, "\n")
|
||||
if dumpmode.X then
|
||||
local regs = {...}
|
||||
if jit.arch == "x64" then
|
||||
for i=1,ngpr do
|
||||
out:write(format(" %016x", regs[i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,ngpr do
|
||||
out:write(format(" %08x", regs[i]))
|
||||
if i % 8 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
if jit.arch == "mips" or jit.arch == "mipsel" then
|
||||
for i=1,nfpr,2 do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 8 == 7 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,nfpr do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_texit)
|
||||
jit.attach(dump_record)
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(opt, outfile)
|
||||
if active then dumpoff() end
|
||||
|
||||
local colormode = os.getenv("COLORTERM") and "A" or "T"
|
||||
if opt then
|
||||
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
|
||||
end
|
||||
|
||||
local m = { t=true, b=true, i=true, m=true, }
|
||||
if opt and opt ~= "" then
|
||||
local o = sub(opt, 1, 1)
|
||||
if o ~= "+" and o ~= "-" then m = {} end
|
||||
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
|
||||
end
|
||||
dumpmode = m
|
||||
|
||||
if m.t or m.b or m.i or m.s or m.m then
|
||||
jit.attach(dump_trace, "trace")
|
||||
end
|
||||
if m.b then
|
||||
jit.attach(dump_record, "record")
|
||||
if not bcline then bcline = require("jit.bc").line end
|
||||
end
|
||||
if m.x or m.X then
|
||||
jit.attach(dump_texit, "texit")
|
||||
end
|
||||
|
||||
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stdout
|
||||
end
|
||||
|
||||
m[colormode] = true
|
||||
if colormode == "A" then
|
||||
colorize = colorize_ansi
|
||||
irtype = irtype_ansi
|
||||
elseif colormode == "H" then
|
||||
colorize = colorize_html
|
||||
irtype = irtype_html
|
||||
out:write(header_html)
|
||||
else
|
||||
colorize = colorize_text
|
||||
irtype = irtype_text
|
||||
end
|
||||
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
on = dumpon
|
||||
off = dumpoff
|
||||
start = dumpon -- For -j command line option.
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Verbose mode of the LuaJIT compiler.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module shows verbose information about the progress of the
|
||||
-- JIT compiler. It prints one line for each generated trace. This module
|
||||
-- is useful to see which code has been compiled or where the compiler
|
||||
-- punts and falls back to the interpreter.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
|
||||
-- luajit -jv=myapp.out myapp.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- The output from the first example should look like this:
|
||||
--
|
||||
-- [TRACE 1 (command line):1 loop]
|
||||
-- [TRACE 2 (1/3) (command line):1 -> 1]
|
||||
--
|
||||
-- The first number in each line is the internal trace number. Next are
|
||||
-- the file name ('(command line)') and the line number (':1') where the
|
||||
-- trace has started. Side traces also show the parent trace number and
|
||||
-- the exit number where they are attached to in parentheses ('(1/3)').
|
||||
-- An arrow at the end shows where the trace links to ('-> 1'), unless
|
||||
-- it loops to itself.
|
||||
--
|
||||
-- In this case the inner loop gets hot and is traced first, generating
|
||||
-- a root trace. Then the last exit from the 1st trace gets hot, too,
|
||||
-- and triggers generation of the 2nd trace. The side trace follows the
|
||||
-- path along the outer loop and *around* the inner loop, back to its
|
||||
-- start, and then links to the 1st trace. Yes, this may seem unusual,
|
||||
-- if you know how traditional compilers work. Trace compilers are full
|
||||
-- of surprises like this -- have fun! :-)
|
||||
--
|
||||
-- Aborted traces are shown like this:
|
||||
--
|
||||
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
|
||||
--
|
||||
-- Don't worry -- trace aborts are quite common, even in programs which
|
||||
-- can be fully compiled. The compiler may retry several times until it
|
||||
-- finds a suitable trace.
|
||||
--
|
||||
-- Of course this doesn't work with features that are not-yet-implemented
|
||||
-- (NYI error messages). The VM simply falls back to the interpreter. This
|
||||
-- may not matter at all if the particular trace is not very high up in
|
||||
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
|
||||
--
|
||||
-- Also check out the -jdump module, which prints all the gory details.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20001, "LuaJIT core/library version mismatch")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
|
||||
local type, format = type, string.format
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local startloc, startex
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
err = format(vmdef.traceerr[err], info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "start" then
|
||||
startloc = fmtfunc(func, pc)
|
||||
startex = otr and "("..otr.."/"..oex..") " or ""
|
||||
else
|
||||
if what == "abort" then
|
||||
local loc = fmtfunc(func, pc)
|
||||
if loc ~= startloc then
|
||||
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
|
||||
startex, startloc, fmterr(otr, oex), loc))
|
||||
else
|
||||
out:write(format("[TRACE --- %s%s -- %s]\n",
|
||||
startex, startloc, fmterr(otr, oex)))
|
||||
end
|
||||
elseif what == "stop" then
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if ltype == "interpreter" then
|
||||
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
|
||||
tr, startex, startloc))
|
||||
elseif link == tr or link == 0 then
|
||||
out:write(format("[TRACE %3s %s%s %s]\n",
|
||||
tr, startex, startloc, ltype))
|
||||
elseif ltype == "root" then
|
||||
out:write(format("[TRACE %3s %s%s -> %d]\n",
|
||||
tr, startex, startloc, link))
|
||||
else
|
||||
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
|
||||
tr, startex, startloc, link, ltype))
|
||||
end
|
||||
else
|
||||
out:write(format("[TRACE %s]\n", what))
|
||||
end
|
||||
out:flush()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(outfile)
|
||||
if active then dumpoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(dump_trace, "trace")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
on = dumpon
|
||||
off = dumpoff
|
||||
start = dumpon -- For -j command line option.
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
** Bit manipulation library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lib_bit_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_bit
|
||||
|
||||
LJLIB_ASM(bit_tobit) LJLIB_REC(bit_unary IR_TOBIT)
|
||||
{
|
||||
lj_lib_checknumber(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(bit_bnot) LJLIB_REC(bit_unary IR_BNOT)
|
||||
LJLIB_ASM_(bit_bswap) LJLIB_REC(bit_unary IR_BSWAP)
|
||||
|
||||
LJLIB_ASM(bit_lshift) LJLIB_REC(bit_shift IR_BSHL)
|
||||
{
|
||||
lj_lib_checknumber(L, 1);
|
||||
lj_lib_checkbit(L, 2);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(bit_rshift) LJLIB_REC(bit_shift IR_BSHR)
|
||||
LJLIB_ASM_(bit_arshift) LJLIB_REC(bit_shift IR_BSAR)
|
||||
LJLIB_ASM_(bit_rol) LJLIB_REC(bit_shift IR_BROL)
|
||||
LJLIB_ASM_(bit_ror) LJLIB_REC(bit_shift IR_BROR)
|
||||
|
||||
LJLIB_ASM(bit_band) LJLIB_REC(bit_nary IR_BAND)
|
||||
{
|
||||
int i = 0;
|
||||
do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(bit_bor) LJLIB_REC(bit_nary IR_BOR)
|
||||
LJLIB_ASM_(bit_bxor) LJLIB_REC(bit_nary IR_BXOR)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
LJLIB_CF(bit_tohex)
|
||||
{
|
||||
uint32_t b = (uint32_t)lj_lib_checkbit(L, 1);
|
||||
int32_t i, n = L->base+1 >= L->top ? 8 : lj_lib_checkbit(L, 2);
|
||||
const char *hexdigits = "0123456789abcdef";
|
||||
char buf[8];
|
||||
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
|
||||
if (n > 8) n = 8;
|
||||
for (i = n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
|
||||
lua_pushlstring(L, buf, (size_t)n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_bit(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_BITLIBNAME, bit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
** Math library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define lib_math_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_lib.h"
|
||||
#include "lj_vm.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_math
|
||||
|
||||
LJLIB_ASM(math_abs) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknumber(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_floor) LJLIB_REC(math_round IRFPM_FLOOR)
|
||||
LJLIB_ASM_(math_ceil) LJLIB_REC(math_round IRFPM_CEIL)
|
||||
|
||||
LJLIB_ASM(math_sqrt) LJLIB_REC(math_unary IRFPM_SQRT)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_log10) LJLIB_REC(math_unary IRFPM_LOG10)
|
||||
LJLIB_ASM_(math_exp) LJLIB_REC(math_unary IRFPM_EXP)
|
||||
LJLIB_ASM_(math_sin) LJLIB_REC(math_unary IRFPM_SIN)
|
||||
LJLIB_ASM_(math_cos) LJLIB_REC(math_unary IRFPM_COS)
|
||||
LJLIB_ASM_(math_tan) LJLIB_REC(math_unary IRFPM_TAN)
|
||||
LJLIB_ASM_(math_asin) LJLIB_REC(math_atrig FF_math_asin)
|
||||
LJLIB_ASM_(math_acos) LJLIB_REC(math_atrig FF_math_acos)
|
||||
LJLIB_ASM_(math_atan) LJLIB_REC(math_atrig FF_math_atan)
|
||||
LJLIB_ASM_(math_sinh) LJLIB_REC(math_htrig IRCALL_sinh)
|
||||
LJLIB_ASM_(math_cosh) LJLIB_REC(math_htrig IRCALL_cosh)
|
||||
LJLIB_ASM_(math_tanh) LJLIB_REC(math_htrig IRCALL_tanh)
|
||||
LJLIB_ASM_(math_frexp)
|
||||
LJLIB_ASM_(math_modf) LJLIB_REC(.)
|
||||
|
||||
LJLIB_ASM(math_log) LJLIB_REC(math_log)
|
||||
{
|
||||
double x = lj_lib_checknum(L, 1);
|
||||
if (L->base+1 < L->top) {
|
||||
double y = lj_lib_checknum(L, 2);
|
||||
#ifdef LUAJIT_NO_LOG2
|
||||
x = log(x); y = 1.0 / log(y);
|
||||
#else
|
||||
x = lj_vm_log2(x); y = 1.0 / lj_vm_log2(y);
|
||||
#endif
|
||||
setnumV(L->base-1, x*y); /* Do NOT join the expression to x / y. */
|
||||
return FFH_RES(1);
|
||||
}
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(57.29577951308232)
|
||||
LJLIB_ASM_(math_deg) LJLIB_REC(math_degrad)
|
||||
|
||||
LJLIB_PUSH(0.017453292519943295)
|
||||
LJLIB_ASM_(math_rad) LJLIB_REC(math_degrad)
|
||||
|
||||
LJLIB_ASM(math_atan2) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
lj_lib_checknum(L, 2);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_pow) LJLIB_REC(.)
|
||||
LJLIB_ASM_(math_fmod)
|
||||
|
||||
LJLIB_ASM(math_ldexp) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
#if LJ_DUALNUM && !LJ_TARGET_X86ORX64
|
||||
lj_lib_checkint(L, 2);
|
||||
#else
|
||||
lj_lib_checknum(L, 2);
|
||||
#endif
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(math_min) LJLIB_REC(math_minmax IR_MIN)
|
||||
{
|
||||
int i = 0;
|
||||
do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_max) LJLIB_REC(math_minmax IR_MAX)
|
||||
|
||||
LJLIB_PUSH(3.14159265358979323846) LJLIB_SET(pi)
|
||||
LJLIB_PUSH(1e310) LJLIB_SET(huge)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* This implements a Tausworthe PRNG with period 2^223. Based on:
|
||||
** Tables of maximally-equidistributed combined LFSR generators,
|
||||
** Pierre L'Ecuyer, 1991, table 3, 1st entry.
|
||||
** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
|
||||
*/
|
||||
|
||||
/* PRNG state. */
|
||||
struct RandomState {
|
||||
uint64_t gen[4]; /* State of the 4 LFSR generators. */
|
||||
int valid; /* State is valid. */
|
||||
};
|
||||
|
||||
/* Union needed for bit-pattern conversion between uint64_t and double. */
|
||||
typedef union { uint64_t u64; double d; } U64double;
|
||||
|
||||
/* Update generator i and compute a running xor of all states. */
|
||||
#define TW223_GEN(i, k, q, s) \
|
||||
z = rs->gen[i]; \
|
||||
z = (((z<<q)^z) >> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<<s); \
|
||||
r ^= z; rs->gen[i] = z;
|
||||
|
||||
/* PRNG step function. Returns a double in the range 1.0 <= d < 2.0. */
|
||||
LJ_NOINLINE uint64_t LJ_FASTCALL lj_math_random_step(RandomState *rs)
|
||||
{
|
||||
uint64_t z, r = 0;
|
||||
TW223_GEN(0, 63, 31, 18)
|
||||
TW223_GEN(1, 58, 19, 28)
|
||||
TW223_GEN(2, 55, 24, 7)
|
||||
TW223_GEN(3, 47, 21, 8)
|
||||
return (r & U64x(000fffff,ffffffff)) | U64x(3ff00000,00000000);
|
||||
}
|
||||
|
||||
/* PRNG initialization function. */
|
||||
static void random_init(RandomState *rs, double d)
|
||||
{
|
||||
uint32_t r = 0x11090601; /* 64-k[i] as four 8 bit constants. */
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
U64double u;
|
||||
uint32_t m = 1u << (r&255);
|
||||
r >>= 8;
|
||||
u.d = d = d * 3.14159265358979323846 + 2.7182818284590452354;
|
||||
if (u.u64 < m) u.u64 += m; /* Ensure k[i] MSB of gen[i] are non-zero. */
|
||||
rs->gen[i] = u.u64;
|
||||
}
|
||||
rs->valid = 1;
|
||||
for (i = 0; i < 10; i++)
|
||||
lj_math_random_step(rs);
|
||||
}
|
||||
|
||||
/* PRNG extract function. */
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with RandomState. */
|
||||
LJLIB_CF(math_random) LJLIB_REC(.)
|
||||
{
|
||||
int n = (int)(L->top - L->base);
|
||||
RandomState *rs = (RandomState *)(uddata(udataV(lj_lib_upvalue(L, 1))));
|
||||
U64double u;
|
||||
double d;
|
||||
if (LJ_UNLIKELY(!rs->valid)) random_init(rs, 0.0);
|
||||
u.u64 = lj_math_random_step(rs);
|
||||
d = u.d - 1.0;
|
||||
if (n > 0) {
|
||||
#if LJ_DUALNUM
|
||||
int isint = 1;
|
||||
double r1;
|
||||
lj_lib_checknumber(L, 1);
|
||||
if (tvisint(L->base)) {
|
||||
r1 = (lua_Number)intV(L->base);
|
||||
} else {
|
||||
isint = 0;
|
||||
r1 = numV(L->base);
|
||||
}
|
||||
#else
|
||||
double r1 = lj_lib_checknum(L, 1);
|
||||
#endif
|
||||
if (n == 1) {
|
||||
d = lj_vm_floor(d*r1) + 1.0; /* d is an int in range [1, r1] */
|
||||
} else {
|
||||
#if LJ_DUALNUM
|
||||
double r2;
|
||||
lj_lib_checknumber(L, 2);
|
||||
if (tvisint(L->base+1)) {
|
||||
r2 = (lua_Number)intV(L->base+1);
|
||||
} else {
|
||||
isint = 0;
|
||||
r2 = numV(L->base+1);
|
||||
}
|
||||
#else
|
||||
double r2 = lj_lib_checknum(L, 2);
|
||||
#endif
|
||||
d = lj_vm_floor(d*(r2-r1+1.0)) + r1; /* d is an int in range [r1, r2] */
|
||||
}
|
||||
#if LJ_DUALNUM
|
||||
if (isint) {
|
||||
setintV(L->top-1, lj_num2int(d));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
} /* else: d is a double in range [0, 1] */
|
||||
setnumV(L->top++, d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* PRNG seed function. */
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with RandomState. */
|
||||
LJLIB_CF(math_randomseed)
|
||||
{
|
||||
RandomState *rs = (RandomState *)(uddata(udataV(lj_lib_upvalue(L, 1))));
|
||||
random_init(rs, lj_lib_checknum(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_math(lua_State *L)
|
||||
{
|
||||
RandomState *rs;
|
||||
rs = (RandomState *)lua_newuserdata(L, sizeof(RandomState));
|
||||
rs->valid = 0; /* Use lazy initialization to save some time on startup. */
|
||||
LJ_LIB_REG(L, LUA_MATHLIBNAME, math);
|
||||
#if defined(LUA_COMPAT_MOD) && !LJ_52
|
||||
lua_getfield(L, -1, "fmod");
|
||||
lua_setfield(L, -2, "mod");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,940 +0,0 @@
|
||||
/*
|
||||
** String library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_string_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_bcdump.h"
|
||||
#include "lj_char.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_string
|
||||
|
||||
LJLIB_ASM(string_len) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checkstr(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_byte) LJLIB_REC(string_range 0)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
int32_t len = (int32_t)s->len;
|
||||
int32_t start = lj_lib_optint(L, 2, 1);
|
||||
int32_t stop = lj_lib_optint(L, 3, start);
|
||||
int32_t n, i;
|
||||
const unsigned char *p;
|
||||
if (stop < 0) stop += len+1;
|
||||
if (start < 0) start += len+1;
|
||||
if (start <= 0) start = 1;
|
||||
if (stop > len) stop = len;
|
||||
if (start > stop) return FFH_RES(0); /* Empty interval: return no results. */
|
||||
start--;
|
||||
n = stop - start;
|
||||
if ((uint32_t)n > LUAI_MAXCSTACK)
|
||||
lj_err_caller(L, LJ_ERR_STRSLC);
|
||||
lj_state_checkstack(L, (MSize)n);
|
||||
p = (const unsigned char *)strdata(s) + start;
|
||||
for (i = 0; i < n; i++)
|
||||
setintV(L->base + i-1, p[i]);
|
||||
return FFH_RES(n);
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_char)
|
||||
{
|
||||
int i, nargs = (int)(L->top - L->base);
|
||||
char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, (size_t)nargs);
|
||||
for (i = 1; i <= nargs; i++) {
|
||||
int32_t k = lj_lib_checkint(L, i);
|
||||
if (!checku8(k))
|
||||
lj_err_arg(L, i, LJ_ERR_BADVAL);
|
||||
buf[i-1] = (char)k;
|
||||
}
|
||||
setstrV(L, L->base-1, lj_str_new(L, buf, (size_t)nargs));
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_sub) LJLIB_REC(string_range 1)
|
||||
{
|
||||
lj_lib_checkstr(L, 1);
|
||||
lj_lib_checkint(L, 2);
|
||||
setintV(L->base+2, lj_lib_optint(L, 3, -1));
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_rep)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
int32_t k = lj_lib_checkint(L, 2);
|
||||
GCstr *sep = lj_lib_optstr(L, 3);
|
||||
int32_t len = (int32_t)s->len;
|
||||
global_State *g = G(L);
|
||||
int64_t tlen;
|
||||
const char *src;
|
||||
char *buf;
|
||||
if (k <= 0) {
|
||||
empty:
|
||||
setstrV(L, L->base-1, &g->strempty);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
if (sep) {
|
||||
tlen = (int64_t)len + sep->len;
|
||||
if (tlen > LJ_MAX_STR)
|
||||
lj_err_caller(L, LJ_ERR_STROV);
|
||||
tlen *= k;
|
||||
if (tlen > LJ_MAX_STR)
|
||||
lj_err_caller(L, LJ_ERR_STROV);
|
||||
} else {
|
||||
tlen = (int64_t)k * len;
|
||||
if (tlen > LJ_MAX_STR)
|
||||
lj_err_caller(L, LJ_ERR_STROV);
|
||||
}
|
||||
if (tlen == 0) goto empty;
|
||||
buf = lj_str_needbuf(L, &g->tmpbuf, (MSize)tlen);
|
||||
src = strdata(s);
|
||||
if (sep) {
|
||||
tlen -= sep->len; /* Ignore trailing separator. */
|
||||
if (k > 1) { /* Paste one string and one separator. */
|
||||
int32_t i;
|
||||
i = 0; while (i < len) *buf++ = src[i++];
|
||||
src = strdata(sep); len = sep->len;
|
||||
i = 0; while (i < len) *buf++ = src[i++];
|
||||
src = g->tmpbuf.buf; len += s->len; k--; /* Now copy that k-1 times. */
|
||||
}
|
||||
}
|
||||
do {
|
||||
int32_t i = 0;
|
||||
do { *buf++ = src[i++]; } while (i < len);
|
||||
} while (--k > 0);
|
||||
setstrV(L, L->base-1, lj_str_new(L, g->tmpbuf.buf, (size_t)tlen));
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_ASM(string_reverse)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
lj_str_needbuf(L, &G(L)->tmpbuf, s->len);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(string_lower)
|
||||
LJLIB_ASM_(string_upper)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static int writer_buf(lua_State *L, const void *p, size_t size, void *b)
|
||||
{
|
||||
luaL_addlstring((luaL_Buffer *)b, (const char *)p, size);
|
||||
UNUSED(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(string_dump)
|
||||
{
|
||||
GCfunc *fn = lj_lib_checkfunc(L, 1);
|
||||
int strip = L->base+1 < L->top && tvistruecond(L->base+1);
|
||||
luaL_Buffer b;
|
||||
L->top = L->base+1;
|
||||
luaL_buffinit(L, &b);
|
||||
if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, &b, strip))
|
||||
lj_err_caller(L, LJ_ERR_STRDUMP);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* macro to `unsign' a character */
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
#define CAP_UNFINISHED (-1)
|
||||
#define CAP_POSITION (-2)
|
||||
|
||||
typedef struct MatchState {
|
||||
const char *src_init; /* init of source string */
|
||||
const char *src_end; /* end (`\0') of source string */
|
||||
lua_State *L;
|
||||
int level; /* total number of captures (finished or unfinished) */
|
||||
int depth;
|
||||
struct {
|
||||
const char *init;
|
||||
ptrdiff_t len;
|
||||
} capture[LUA_MAXCAPTURES];
|
||||
} MatchState;
|
||||
|
||||
#define L_ESC '%'
|
||||
#define SPECIALS "^$*+?.([%-"
|
||||
|
||||
static int check_capture(MatchState *ms, int l)
|
||||
{
|
||||
l -= '1';
|
||||
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
|
||||
return l;
|
||||
}
|
||||
|
||||
static int capture_to_close(MatchState *ms)
|
||||
{
|
||||
int level = ms->level;
|
||||
for (level--; level>=0; level--)
|
||||
if (ms->capture[level].len == CAP_UNFINISHED) return level;
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATC);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
static const char *classend(MatchState *ms, const char *p)
|
||||
{
|
||||
switch (*p++) {
|
||||
case L_ESC:
|
||||
if (*p == '\0')
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATE);
|
||||
return p+1;
|
||||
case '[':
|
||||
if (*p == '^') p++;
|
||||
do { /* look for a `]' */
|
||||
if (*p == '\0')
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATM);
|
||||
if (*(p++) == L_ESC && *p != '\0')
|
||||
p++; /* skip escapes (e.g. `%]') */
|
||||
} while (*p != ']');
|
||||
return p+1;
|
||||
default:
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char match_class_map[32] = {
|
||||
0,LJ_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0,
|
||||
LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0,
|
||||
LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static int match_class(int c, int cl)
|
||||
{
|
||||
if ((cl & 0xc0) == 0x40) {
|
||||
int t = match_class_map[(cl&0x1f)];
|
||||
if (t) {
|
||||
t = lj_char_isa(c, t);
|
||||
return (cl & 0x20) ? t : !t;
|
||||
}
|
||||
if (cl == 'z') return c == 0;
|
||||
if (cl == 'Z') return c != 0;
|
||||
}
|
||||
return (cl == c);
|
||||
}
|
||||
|
||||
static int matchbracketclass(int c, const char *p, const char *ec)
|
||||
{
|
||||
int sig = 1;
|
||||
if (*(p+1) == '^') {
|
||||
sig = 0;
|
||||
p++; /* skip the `^' */
|
||||
}
|
||||
while (++p < ec) {
|
||||
if (*p == L_ESC) {
|
||||
p++;
|
||||
if (match_class(c, uchar(*p)))
|
||||
return sig;
|
||||
}
|
||||
else if ((*(p+1) == '-') && (p+2 < ec)) {
|
||||
p+=2;
|
||||
if (uchar(*(p-2)) <= c && c <= uchar(*p))
|
||||
return sig;
|
||||
}
|
||||
else if (uchar(*p) == c) return sig;
|
||||
}
|
||||
return !sig;
|
||||
}
|
||||
|
||||
static int singlematch(int c, const char *p, const char *ep)
|
||||
{
|
||||
switch (*p) {
|
||||
case '.': return 1; /* matches any char */
|
||||
case L_ESC: return match_class(c, uchar(*(p+1)));
|
||||
case '[': return matchbracketclass(c, p, ep-1);
|
||||
default: return (uchar(*p) == c);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *match(MatchState *ms, const char *s, const char *p);
|
||||
|
||||
static const char *matchbalance(MatchState *ms, const char *s, const char *p)
|
||||
{
|
||||
if (*p == 0 || *(p+1) == 0)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATU);
|
||||
if (*s != *p) {
|
||||
return NULL;
|
||||
} else {
|
||||
int b = *p;
|
||||
int e = *(p+1);
|
||||
int cont = 1;
|
||||
while (++s < ms->src_end) {
|
||||
if (*s == e) {
|
||||
if (--cont == 0) return s+1;
|
||||
} else if (*s == b) {
|
||||
cont++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; /* string ends out of balance */
|
||||
}
|
||||
|
||||
static const char *max_expand(MatchState *ms, const char *s,
|
||||
const char *p, const char *ep)
|
||||
{
|
||||
ptrdiff_t i = 0; /* counts maximum expand for item */
|
||||
while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
|
||||
i++;
|
||||
/* keeps trying to match with the maximum repetitions */
|
||||
while (i>=0) {
|
||||
const char *res = match(ms, (s+i), ep+1);
|
||||
if (res) return res;
|
||||
i--; /* else didn't match; reduce 1 repetition to try again */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *min_expand(MatchState *ms, const char *s,
|
||||
const char *p, const char *ep)
|
||||
{
|
||||
for (;;) {
|
||||
const char *res = match(ms, s, ep+1);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
|
||||
s++; /* try with one more repetition */
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *start_capture(MatchState *ms, const char *s,
|
||||
const char *p, int what)
|
||||
{
|
||||
const char *res;
|
||||
int level = ms->level;
|
||||
if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
|
||||
ms->capture[level].init = s;
|
||||
ms->capture[level].len = what;
|
||||
ms->level = level+1;
|
||||
if ((res=match(ms, s, p)) == NULL) /* match failed? */
|
||||
ms->level--; /* undo capture */
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *end_capture(MatchState *ms, const char *s,
|
||||
const char *p)
|
||||
{
|
||||
int l = capture_to_close(ms);
|
||||
const char *res;
|
||||
ms->capture[l].len = s - ms->capture[l].init; /* close capture */
|
||||
if ((res = match(ms, s, p)) == NULL) /* match failed? */
|
||||
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *match_capture(MatchState *ms, const char *s, int l)
|
||||
{
|
||||
size_t len;
|
||||
l = check_capture(ms, l);
|
||||
len = (size_t)ms->capture[l].len;
|
||||
if ((size_t)(ms->src_end-s) >= len &&
|
||||
memcmp(ms->capture[l].init, s, len) == 0)
|
||||
return s+len;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *match(MatchState *ms, const char *s, const char *p)
|
||||
{
|
||||
if (++ms->depth > LJ_MAX_XLEVEL)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATX);
|
||||
init: /* using goto's to optimize tail recursion */
|
||||
switch (*p) {
|
||||
case '(': /* start capture */
|
||||
if (*(p+1) == ')') /* position capture? */
|
||||
s = start_capture(ms, s, p+2, CAP_POSITION);
|
||||
else
|
||||
s = start_capture(ms, s, p+1, CAP_UNFINISHED);
|
||||
break;
|
||||
case ')': /* end capture */
|
||||
s = end_capture(ms, s, p+1);
|
||||
break;
|
||||
case L_ESC:
|
||||
switch (*(p+1)) {
|
||||
case 'b': /* balanced string? */
|
||||
s = matchbalance(ms, s, p+2);
|
||||
if (s == NULL) break;
|
||||
p+=4;
|
||||
goto init; /* else s = match(ms, s, p+4); */
|
||||
case 'f': { /* frontier? */
|
||||
const char *ep; char previous;
|
||||
p += 2;
|
||||
if (*p != '[')
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATB);
|
||||
ep = classend(ms, p); /* points to what is next */
|
||||
previous = (s == ms->src_init) ? '\0' : *(s-1);
|
||||
if (matchbracketclass(uchar(previous), p, ep-1) ||
|
||||
!matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; }
|
||||
p=ep;
|
||||
goto init; /* else s = match(ms, s, ep); */
|
||||
}
|
||||
default:
|
||||
if (lj_char_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
|
||||
s = match_capture(ms, s, uchar(*(p+1)));
|
||||
if (s == NULL) break;
|
||||
p+=2;
|
||||
goto init; /* else s = match(ms, s, p+2) */
|
||||
}
|
||||
goto dflt; /* case default */
|
||||
}
|
||||
break;
|
||||
case '\0': /* end of pattern */
|
||||
break; /* match succeeded */
|
||||
case '$':
|
||||
/* is the `$' the last char in pattern? */
|
||||
if (*(p+1) != '\0') goto dflt;
|
||||
if (s != ms->src_end) s = NULL; /* check end of string */
|
||||
break;
|
||||
default: dflt: { /* it is a pattern item */
|
||||
const char *ep = classend(ms, p); /* points to what is next */
|
||||
int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
|
||||
switch (*ep) {
|
||||
case '?': { /* optional */
|
||||
const char *res;
|
||||
if (m && ((res=match(ms, s+1, ep+1)) != NULL)) {
|
||||
s = res;
|
||||
break;
|
||||
}
|
||||
p=ep+1;
|
||||
goto init; /* else s = match(ms, s, ep+1); */
|
||||
}
|
||||
case '*': /* 0 or more repetitions */
|
||||
s = max_expand(ms, s, p, ep);
|
||||
break;
|
||||
case '+': /* 1 or more repetitions */
|
||||
s = (m ? max_expand(ms, s+1, p, ep) : NULL);
|
||||
break;
|
||||
case '-': /* 0 or more repetitions (minimum) */
|
||||
s = min_expand(ms, s, p, ep);
|
||||
break;
|
||||
default:
|
||||
if (m) { s++; p=ep; goto init; } /* else s = match(ms, s+1, ep); */
|
||||
s = NULL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ms->depth--;
|
||||
return s;
|
||||
}
|
||||
|
||||
static const char *lmemfind(const char *s1, size_t l1,
|
||||
const char *s2, size_t l2)
|
||||
{
|
||||
if (l2 == 0) {
|
||||
return s1; /* empty strings are everywhere */
|
||||
} else if (l2 > l1) {
|
||||
return NULL; /* avoids a negative `l1' */
|
||||
} else {
|
||||
const char *init; /* to search for a `*s2' inside `s1' */
|
||||
l2--; /* 1st char will be checked by `memchr' */
|
||||
l1 = l1-l2; /* `s2' cannot be found after that */
|
||||
while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
|
||||
init++; /* 1st char is already checked */
|
||||
if (memcmp(init, s2+1, l2) == 0) {
|
||||
return init-1;
|
||||
} else { /* correct `l1' and `s1' to try again */
|
||||
l1 -= (size_t)(init-s1);
|
||||
s1 = init;
|
||||
}
|
||||
}
|
||||
return NULL; /* not found */
|
||||
}
|
||||
}
|
||||
|
||||
static void push_onecapture(MatchState *ms, int i, const char *s, const char *e)
|
||||
{
|
||||
if (i >= ms->level) {
|
||||
if (i == 0) /* ms->level == 0, too */
|
||||
lua_pushlstring(ms->L, s, (size_t)(e - s)); /* add whole match */
|
||||
else
|
||||
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
|
||||
} else {
|
||||
ptrdiff_t l = ms->capture[i].len;
|
||||
if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU);
|
||||
if (l == CAP_POSITION)
|
||||
lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
|
||||
else
|
||||
lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l);
|
||||
}
|
||||
}
|
||||
|
||||
static int push_captures(MatchState *ms, const char *s, const char *e)
|
||||
{
|
||||
int i;
|
||||
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
|
||||
luaL_checkstack(ms->L, nlevels, "too many captures");
|
||||
for (i = 0; i < nlevels; i++)
|
||||
push_onecapture(ms, i, s, e);
|
||||
return nlevels; /* number of strings pushed */
|
||||
}
|
||||
|
||||
static ptrdiff_t posrelat(ptrdiff_t pos, size_t len)
|
||||
{
|
||||
/* relative string position: negative means back from end */
|
||||
if (pos < 0) pos += (ptrdiff_t)len + 1;
|
||||
return (pos >= 0) ? pos : 0;
|
||||
}
|
||||
|
||||
static int str_find_aux(lua_State *L, int find)
|
||||
{
|
||||
size_t l1, l2;
|
||||
const char *s = luaL_checklstring(L, 1, &l1);
|
||||
const char *p = luaL_checklstring(L, 2, &l2);
|
||||
ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
|
||||
if (init < 0) {
|
||||
init = 0;
|
||||
} else if ((size_t)(init) > l1) {
|
||||
#if LJ_52
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
#else
|
||||
init = (ptrdiff_t)l1;
|
||||
#endif
|
||||
}
|
||||
if (find && (lua_toboolean(L, 4) || /* explicit request? */
|
||||
strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
|
||||
/* do a plain search */
|
||||
const char *s2 = lmemfind(s+init, l1-(size_t)init, p, l2);
|
||||
if (s2) {
|
||||
lua_pushinteger(L, s2-s+1);
|
||||
lua_pushinteger(L, s2-s+(ptrdiff_t)l2);
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
MatchState ms;
|
||||
int anchor = (*p == '^') ? (p++, 1) : 0;
|
||||
const char *s1=s+init;
|
||||
ms.L = L;
|
||||
ms.src_init = s;
|
||||
ms.src_end = s+l1;
|
||||
do {
|
||||
const char *res;
|
||||
ms.level = ms.depth = 0;
|
||||
if ((res=match(&ms, s1, p)) != NULL) {
|
||||
if (find) {
|
||||
lua_pushinteger(L, s1-s+1); /* start */
|
||||
lua_pushinteger(L, res-s); /* end */
|
||||
return push_captures(&ms, NULL, 0) + 2;
|
||||
} else {
|
||||
return push_captures(&ms, s1, res);
|
||||
}
|
||||
}
|
||||
} while (s1++ < ms.src_end && !anchor);
|
||||
}
|
||||
lua_pushnil(L); /* not found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(string_find)
|
||||
{
|
||||
return str_find_aux(L, 1);
|
||||
}
|
||||
|
||||
LJLIB_CF(string_match)
|
||||
{
|
||||
return str_find_aux(L, 0);
|
||||
}
|
||||
|
||||
LJLIB_NOREG LJLIB_CF(string_gmatch_aux)
|
||||
{
|
||||
const char *p = strVdata(lj_lib_upvalue(L, 2));
|
||||
GCstr *str = strV(lj_lib_upvalue(L, 1));
|
||||
const char *s = strdata(str);
|
||||
TValue *tvpos = lj_lib_upvalue(L, 3);
|
||||
const char *src = s + tvpos->u32.lo;
|
||||
MatchState ms;
|
||||
ms.L = L;
|
||||
ms.src_init = s;
|
||||
ms.src_end = s + str->len;
|
||||
for (; src <= ms.src_end; src++) {
|
||||
const char *e;
|
||||
ms.level = ms.depth = 0;
|
||||
if ((e = match(&ms, src, p)) != NULL) {
|
||||
int32_t pos = (int32_t)(e - s);
|
||||
if (e == src) pos++; /* Ensure progress for empty match. */
|
||||
tvpos->u32.lo = (uint32_t)pos;
|
||||
return push_captures(&ms, src, e);
|
||||
}
|
||||
}
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
LJLIB_CF(string_gmatch)
|
||||
{
|
||||
lj_lib_checkstr(L, 1);
|
||||
lj_lib_checkstr(L, 2);
|
||||
L->top = L->base+3;
|
||||
(L->top-1)->u64 = 0;
|
||||
lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
|
||||
{
|
||||
size_t l, i;
|
||||
const char *news = lua_tolstring(ms->L, 3, &l);
|
||||
for (i = 0; i < l; i++) {
|
||||
if (news[i] != L_ESC) {
|
||||
luaL_addchar(b, news[i]);
|
||||
} else {
|
||||
i++; /* skip ESC */
|
||||
if (!lj_char_isdigit(uchar(news[i]))) {
|
||||
luaL_addchar(b, news[i]);
|
||||
} else if (news[i] == '0') {
|
||||
luaL_addlstring(b, s, (size_t)(e - s));
|
||||
} else {
|
||||
push_onecapture(ms, news[i] - '1', s, e);
|
||||
luaL_addvalue(b); /* add capture to accumulated result */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_value(MatchState *ms, luaL_Buffer *b,
|
||||
const char *s, const char *e)
|
||||
{
|
||||
lua_State *L = ms->L;
|
||||
switch (lua_type(L, 3)) {
|
||||
case LUA_TNUMBER:
|
||||
case LUA_TSTRING: {
|
||||
add_s(ms, b, s, e);
|
||||
return;
|
||||
}
|
||||
case LUA_TFUNCTION: {
|
||||
int n;
|
||||
lua_pushvalue(L, 3);
|
||||
n = push_captures(ms, s, e);
|
||||
lua_call(L, n, 1);
|
||||
break;
|
||||
}
|
||||
case LUA_TTABLE: {
|
||||
push_onecapture(ms, 0, s, e);
|
||||
lua_gettable(L, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!lua_toboolean(L, -1)) { /* nil or false? */
|
||||
lua_pop(L, 1);
|
||||
lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */
|
||||
} else if (!lua_isstring(L, -1)) {
|
||||
lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
|
||||
}
|
||||
luaL_addvalue(b); /* add result to accumulator */
|
||||
}
|
||||
|
||||
LJLIB_CF(string_gsub)
|
||||
{
|
||||
size_t srcl;
|
||||
const char *src = luaL_checklstring(L, 1, &srcl);
|
||||
const char *p = luaL_checkstring(L, 2);
|
||||
int tr = lua_type(L, 3);
|
||||
int max_s = luaL_optint(L, 4, (int)(srcl+1));
|
||||
int anchor = (*p == '^') ? (p++, 1) : 0;
|
||||
int n = 0;
|
||||
MatchState ms;
|
||||
luaL_Buffer b;
|
||||
if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
|
||||
tr == LUA_TFUNCTION || tr == LUA_TTABLE))
|
||||
lj_err_arg(L, 3, LJ_ERR_NOSFT);
|
||||
luaL_buffinit(L, &b);
|
||||
ms.L = L;
|
||||
ms.src_init = src;
|
||||
ms.src_end = src+srcl;
|
||||
while (n < max_s) {
|
||||
const char *e;
|
||||
ms.level = ms.depth = 0;
|
||||
e = match(&ms, src, p);
|
||||
if (e) {
|
||||
n++;
|
||||
add_value(&ms, &b, src, e);
|
||||
}
|
||||
if (e && e>src) /* non empty match? */
|
||||
src = e; /* skip it */
|
||||
else if (src < ms.src_end)
|
||||
luaL_addchar(&b, *src++);
|
||||
else
|
||||
break;
|
||||
if (anchor)
|
||||
break;
|
||||
}
|
||||
luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
|
||||
luaL_pushresult(&b);
|
||||
lua_pushinteger(L, n); /* number of substitutions */
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
|
||||
#define MAX_FMTITEM 512
|
||||
/* valid flags in a format specification */
|
||||
#define FMT_FLAGS "-+ #0"
|
||||
/*
|
||||
** maximum size of each format specification (such as '%-099.99d')
|
||||
** (+10 accounts for %99.99x plus margin of error)
|
||||
*/
|
||||
#define MAX_FMTSPEC (sizeof(FMT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
|
||||
|
||||
static void addquoted(lua_State *L, luaL_Buffer *b, int arg)
|
||||
{
|
||||
GCstr *str = lj_lib_checkstr(L, arg);
|
||||
int32_t len = (int32_t)str->len;
|
||||
const char *s = strdata(str);
|
||||
luaL_addchar(b, '"');
|
||||
while (len--) {
|
||||
uint32_t c = uchar(*s);
|
||||
if (c == '"' || c == '\\' || c == '\n') {
|
||||
luaL_addchar(b, '\\');
|
||||
} else if (lj_char_iscntrl(c)) { /* This can only be 0-31 or 127. */
|
||||
uint32_t d;
|
||||
luaL_addchar(b, '\\');
|
||||
if (c >= 100 || lj_char_isdigit(uchar(s[1]))) {
|
||||
luaL_addchar(b, '0'+(c >= 100)); if (c >= 100) c -= 100;
|
||||
goto tens;
|
||||
} else if (c >= 10) {
|
||||
tens:
|
||||
d = (c * 205) >> 11; c -= d * 10; luaL_addchar(b, '0'+d);
|
||||
}
|
||||
c += '0';
|
||||
}
|
||||
luaL_addchar(b, c);
|
||||
s++;
|
||||
}
|
||||
luaL_addchar(b, '"');
|
||||
}
|
||||
|
||||
static const char *scanformat(lua_State *L, const char *strfrmt, char *form)
|
||||
{
|
||||
const char *p = strfrmt;
|
||||
while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL) p++; /* skip flags */
|
||||
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS))
|
||||
lj_err_caller(L, LJ_ERR_STRFMTR);
|
||||
if (lj_char_isdigit(uchar(*p))) p++; /* skip width */
|
||||
if (lj_char_isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
if (lj_char_isdigit(uchar(*p))) p++; /* skip precision */
|
||||
if (lj_char_isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
}
|
||||
if (lj_char_isdigit(uchar(*p)))
|
||||
lj_err_caller(L, LJ_ERR_STRFMTW);
|
||||
*(form++) = '%';
|
||||
strncpy(form, strfrmt, (size_t)(p - strfrmt + 1));
|
||||
form += p - strfrmt + 1;
|
||||
*form = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
static void addintlen(char *form)
|
||||
{
|
||||
size_t l = strlen(form);
|
||||
char spec = form[l - 1];
|
||||
strcpy(form + l - 1, LUA_INTFRMLEN);
|
||||
form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
|
||||
form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
|
||||
}
|
||||
|
||||
static unsigned LUA_INTFRM_T num2intfrm(lua_State *L, int arg)
|
||||
{
|
||||
if (sizeof(LUA_INTFRM_T) == 4) {
|
||||
return (LUA_INTFRM_T)lj_lib_checkbit(L, arg);
|
||||
} else {
|
||||
cTValue *o;
|
||||
lj_lib_checknumber(L, arg);
|
||||
o = L->base+arg-1;
|
||||
if (tvisint(o))
|
||||
return (LUA_INTFRM_T)intV(o);
|
||||
else
|
||||
return (LUA_INTFRM_T)numV(o);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned LUA_INTFRM_T num2uintfrm(lua_State *L, int arg)
|
||||
{
|
||||
if (sizeof(LUA_INTFRM_T) == 4) {
|
||||
return (unsigned LUA_INTFRM_T)lj_lib_checkbit(L, arg);
|
||||
} else {
|
||||
cTValue *o;
|
||||
lj_lib_checknumber(L, arg);
|
||||
o = L->base+arg-1;
|
||||
if (tvisint(o))
|
||||
return (unsigned LUA_INTFRM_T)intV(o);
|
||||
else if ((int32_t)o->u32.hi < 0)
|
||||
return (unsigned LUA_INTFRM_T)(LUA_INTFRM_T)numV(o);
|
||||
else
|
||||
return (unsigned LUA_INTFRM_T)numV(o);
|
||||
}
|
||||
}
|
||||
|
||||
static GCstr *meta_tostring(lua_State *L, int arg)
|
||||
{
|
||||
TValue *o = L->base+arg-1;
|
||||
cTValue *mo;
|
||||
lua_assert(o < L->top); /* Caller already checks for existence. */
|
||||
if (LJ_LIKELY(tvisstr(o)))
|
||||
return strV(o);
|
||||
if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
|
||||
copyTV(L, L->top++, mo);
|
||||
copyTV(L, L->top++, o);
|
||||
lua_call(L, 1, 1);
|
||||
L->top--;
|
||||
if (tvisstr(L->top))
|
||||
return strV(L->top);
|
||||
o = L->base+arg-1;
|
||||
copyTV(L, o, L->top);
|
||||
}
|
||||
if (tvisnumber(o)) {
|
||||
return lj_str_fromnumber(L, o);
|
||||
} else if (tvisnil(o)) {
|
||||
return lj_str_newlit(L, "nil");
|
||||
} else if (tvisfalse(o)) {
|
||||
return lj_str_newlit(L, "false");
|
||||
} else if (tvistrue(o)) {
|
||||
return lj_str_newlit(L, "true");
|
||||
} else {
|
||||
if (tvisfunc(o) && isffunc(funcV(o)))
|
||||
lj_str_pushf(L, "function: builtin#%d", funcV(o)->c.ffid);
|
||||
else
|
||||
lj_str_pushf(L, "%s: %p", lj_typename(o), lua_topointer(L, arg));
|
||||
L->top--;
|
||||
return strV(L->top);
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(string_format)
|
||||
{
|
||||
int arg = 1, top = (int)(L->top - L->base);
|
||||
GCstr *fmt = lj_lib_checkstr(L, arg);
|
||||
const char *strfrmt = strdata(fmt);
|
||||
const char *strfrmt_end = strfrmt + fmt->len;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
while (strfrmt < strfrmt_end) {
|
||||
if (*strfrmt != L_ESC) {
|
||||
luaL_addchar(&b, *strfrmt++);
|
||||
} else if (*++strfrmt == L_ESC) {
|
||||
luaL_addchar(&b, *strfrmt++); /* %% */
|
||||
} else { /* format item */
|
||||
char form[MAX_FMTSPEC]; /* to store the format (`%...') */
|
||||
char buff[MAX_FMTITEM]; /* to store the formatted item */
|
||||
if (++arg > top)
|
||||
luaL_argerror(L, arg, lj_obj_typename[0]);
|
||||
strfrmt = scanformat(L, strfrmt, form);
|
||||
switch (*strfrmt++) {
|
||||
case 'c':
|
||||
sprintf(buff, form, lj_lib_checkint(L, arg));
|
||||
break;
|
||||
case 'd': case 'i':
|
||||
addintlen(form);
|
||||
sprintf(buff, form, num2intfrm(L, arg));
|
||||
break;
|
||||
case 'o': case 'u': case 'x': case 'X':
|
||||
addintlen(form);
|
||||
sprintf(buff, form, num2uintfrm(L, arg));
|
||||
break;
|
||||
case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': case 'A': {
|
||||
TValue tv;
|
||||
tv.n = lj_lib_checknum(L, arg);
|
||||
if (LJ_UNLIKELY((tv.u32.hi << 1) >= 0xffe00000)) {
|
||||
/* Canonicalize output of non-finite values. */
|
||||
char *p, nbuf[LJ_STR_NUMBUF];
|
||||
size_t len = lj_str_bufnum(nbuf, &tv);
|
||||
if (strfrmt[-1] < 'a') {
|
||||
nbuf[len-3] = nbuf[len-3] - 0x20;
|
||||
nbuf[len-2] = nbuf[len-2] - 0x20;
|
||||
nbuf[len-1] = nbuf[len-1] - 0x20;
|
||||
}
|
||||
nbuf[len] = '\0';
|
||||
for (p = form; *p < 'A' && *p != '.'; p++) ;
|
||||
*p++ = 's'; *p = '\0';
|
||||
sprintf(buff, form, nbuf);
|
||||
break;
|
||||
}
|
||||
sprintf(buff, form, (double)tv.n);
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
addquoted(L, &b, arg);
|
||||
continue;
|
||||
case 'p':
|
||||
lj_str_pushf(L, "%p", lua_topointer(L, arg));
|
||||
luaL_addvalue(&b);
|
||||
continue;
|
||||
case 's': {
|
||||
GCstr *str = meta_tostring(L, arg);
|
||||
if (!strchr(form, '.') && str->len >= 100) {
|
||||
/* no precision and string is too long to be formatted;
|
||||
keep original string */
|
||||
setstrV(L, L->top++, str);
|
||||
luaL_addvalue(&b);
|
||||
continue;
|
||||
}
|
||||
sprintf(buff, form, strdata(str));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
lj_err_callerv(L, LJ_ERR_STRFMTO, *(strfrmt -1));
|
||||
break;
|
||||
}
|
||||
luaL_addlstring(&b, buff, strlen(buff));
|
||||
}
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_string(lua_State *L)
|
||||
{
|
||||
GCtab *mt;
|
||||
global_State *g;
|
||||
LJ_LIB_REG(L, LUA_STRLIBNAME, string);
|
||||
#if defined(LUA_COMPAT_GFIND) && !LJ_52
|
||||
lua_getfield(L, -1, "gmatch");
|
||||
lua_setfield(L, -2, "gfind");
|
||||
#endif
|
||||
mt = lj_tab_new(L, 0, 1);
|
||||
/* NOBARRIER: basemt is a GC root. */
|
||||
g = G(L);
|
||||
setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt));
|
||||
settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1));
|
||||
mt->nomm = (uint8_t)(~(1u<<MM_index));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
** Table library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_table_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_table
|
||||
|
||||
LJLIB_CF(table_foreachi)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
GCfunc *func = lj_lib_checkfunc(L, 2);
|
||||
MSize i, n = lj_tab_len(t);
|
||||
for (i = 1; i <= n; i++) {
|
||||
cTValue *val;
|
||||
setfuncV(L, L->top, func);
|
||||
setintV(L->top+1, i);
|
||||
val = lj_tab_getint(t, (int32_t)i);
|
||||
if (val) { copyTV(L, L->top+2, val); } else { setnilV(L->top+2); }
|
||||
L->top += 3;
|
||||
lua_call(L, 2, 1);
|
||||
if (!tvisnil(L->top-1))
|
||||
return 1;
|
||||
L->top--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(table_foreach)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
GCfunc *func = lj_lib_checkfunc(L, 2);
|
||||
L->top = L->base+3;
|
||||
setnilV(L->top-1);
|
||||
while (lj_tab_next(L, t, L->top-1)) {
|
||||
copyTV(L, L->top+2, L->top);
|
||||
copyTV(L, L->top+1, L->top-1);
|
||||
setfuncV(L, L->top, func);
|
||||
L->top += 3;
|
||||
lua_call(L, 2, 1);
|
||||
if (!tvisnil(L->top-1))
|
||||
return 1;
|
||||
L->top--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_ASM(table_getn) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
LJLIB_CF(table_maxn)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
TValue *array = tvref(t->array);
|
||||
Node *node;
|
||||
lua_Number m = 0;
|
||||
ptrdiff_t i;
|
||||
for (i = (ptrdiff_t)t->asize - 1; i >= 0; i--)
|
||||
if (!tvisnil(&array[i])) {
|
||||
m = (lua_Number)(int32_t)i;
|
||||
break;
|
||||
}
|
||||
node = noderef(t->node);
|
||||
for (i = (ptrdiff_t)t->hmask; i >= 0; i--)
|
||||
if (!tvisnil(&node[i].val) && tvisnumber(&node[i].key)) {
|
||||
lua_Number n = numberVnum(&node[i].key);
|
||||
if (n > m) m = n;
|
||||
}
|
||||
setnumV(L->top-1, m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(table_insert) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n, i = (int32_t)lj_tab_len(t) + 1;
|
||||
int nargs = (int)((char *)L->top - (char *)L->base);
|
||||
if (nargs != 2*sizeof(TValue)) {
|
||||
if (nargs != 3*sizeof(TValue))
|
||||
lj_err_caller(L, LJ_ERR_TABINS);
|
||||
/* NOBARRIER: This just moves existing elements around. */
|
||||
for (n = lj_lib_checkint(L, 2); i > n; i--) {
|
||||
/* The set may invalidate the get pointer, so need to do it first! */
|
||||
TValue *dst = lj_tab_setint(L, t, i);
|
||||
cTValue *src = lj_tab_getint(t, i-1);
|
||||
if (src) {
|
||||
copyTV(L, dst, src);
|
||||
} else {
|
||||
setnilV(dst);
|
||||
}
|
||||
}
|
||||
i = n;
|
||||
}
|
||||
{
|
||||
TValue *dst = lj_tab_setint(L, t, i);
|
||||
copyTV(L, dst, L->top-1); /* Set new value. */
|
||||
lj_gc_barriert(L, t, dst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(table_remove) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t e = (int32_t)lj_tab_len(t);
|
||||
int32_t pos = lj_lib_optint(L, 2, e);
|
||||
if (!(1 <= pos && pos <= e)) /* Nothing to remove? */
|
||||
return 0;
|
||||
lua_rawgeti(L, 1, pos); /* Get previous value. */
|
||||
/* NOBARRIER: This just moves existing elements around. */
|
||||
for (; pos < e; pos++) {
|
||||
cTValue *src = lj_tab_getint(t, pos+1);
|
||||
TValue *dst = lj_tab_setint(L, t, pos);
|
||||
if (src) {
|
||||
copyTV(L, dst, src);
|
||||
} else {
|
||||
setnilV(dst);
|
||||
}
|
||||
}
|
||||
setnilV(lj_tab_setint(L, t, e)); /* Remove (last) value. */
|
||||
return 1; /* Return previous value. */
|
||||
}
|
||||
|
||||
LJLIB_CF(table_concat)
|
||||
{
|
||||
luaL_Buffer b;
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
GCstr *sep = lj_lib_optstr(L, 2);
|
||||
MSize seplen = sep ? sep->len : 0;
|
||||
int32_t i = lj_lib_optint(L, 3, 1);
|
||||
int32_t e = L->base+3 < L->top ? lj_lib_checkint(L, 4) :
|
||||
(int32_t)lj_tab_len(t);
|
||||
luaL_buffinit(L, &b);
|
||||
if (i <= e) {
|
||||
for (;;) {
|
||||
cTValue *o;
|
||||
lua_rawgeti(L, 1, i);
|
||||
o = L->top-1;
|
||||
if (!(tvisstr(o) || tvisnumber(o)))
|
||||
lj_err_callerv(L, LJ_ERR_TABCAT, lj_typename(o), i);
|
||||
luaL_addvalue(&b);
|
||||
if (i++ == e) break;
|
||||
if (seplen)
|
||||
luaL_addlstring(&b, strdata(sep), seplen);
|
||||
}
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void set2(lua_State *L, int i, int j)
|
||||
{
|
||||
lua_rawseti(L, 1, i);
|
||||
lua_rawseti(L, 1, j);
|
||||
}
|
||||
|
||||
static int sort_comp(lua_State *L, int a, int b)
|
||||
{
|
||||
if (!lua_isnil(L, 2)) { /* function? */
|
||||
int res;
|
||||
lua_pushvalue(L, 2);
|
||||
lua_pushvalue(L, a-1); /* -1 to compensate function */
|
||||
lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
|
||||
lua_call(L, 2, 1);
|
||||
res = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
} else { /* a < b? */
|
||||
return lua_lessthan(L, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
static void auxsort(lua_State *L, int l, int u)
|
||||
{
|
||||
while (l < u) { /* for tail recursion */
|
||||
int i, j;
|
||||
/* sort elements a[l], a[(l+u)/2] and a[u] */
|
||||
lua_rawgeti(L, 1, l);
|
||||
lua_rawgeti(L, 1, u);
|
||||
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
|
||||
set2(L, l, u); /* swap a[l] - a[u] */
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
if (u-l == 1) break; /* only 2 elements */
|
||||
i = (l+u)/2;
|
||||
lua_rawgeti(L, 1, i);
|
||||
lua_rawgeti(L, 1, l);
|
||||
if (sort_comp(L, -2, -1)) { /* a[i]<a[l]? */
|
||||
set2(L, i, l);
|
||||
} else {
|
||||
lua_pop(L, 1); /* remove a[l] */
|
||||
lua_rawgeti(L, 1, u);
|
||||
if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
|
||||
set2(L, i, u);
|
||||
else
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
if (u-l == 2) break; /* only 3 elements */
|
||||
lua_rawgeti(L, 1, i); /* Pivot */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawgeti(L, 1, u-1);
|
||||
set2(L, i, u-1);
|
||||
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
|
||||
i = l; j = u-1;
|
||||
for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
|
||||
/* repeat ++i until a[i] >= P */
|
||||
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
||||
if (i>=u) lj_err_caller(L, LJ_ERR_TABSORT);
|
||||
lua_pop(L, 1); /* remove a[i] */
|
||||
}
|
||||
/* repeat --j until a[j] <= P */
|
||||
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
|
||||
if (j<=l) lj_err_caller(L, LJ_ERR_TABSORT);
|
||||
lua_pop(L, 1); /* remove a[j] */
|
||||
}
|
||||
if (j<i) {
|
||||
lua_pop(L, 3); /* pop pivot, a[i], a[j] */
|
||||
break;
|
||||
}
|
||||
set2(L, i, j);
|
||||
}
|
||||
lua_rawgeti(L, 1, u-1);
|
||||
lua_rawgeti(L, 1, i);
|
||||
set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
|
||||
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */
|
||||
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */
|
||||
if (i-l < u-i) {
|
||||
j=l; i=i-1; l=i+2;
|
||||
} else {
|
||||
j=i+1; i=u; u=j-2;
|
||||
}
|
||||
auxsort(L, j, i); /* call recursively the smaller one */
|
||||
} /* repeat the routine for the larger one */
|
||||
}
|
||||
|
||||
LJLIB_CF(table_sort)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n = (int32_t)lj_tab_len(t);
|
||||
lua_settop(L, 2);
|
||||
if (!tvisnil(L->base+1))
|
||||
lj_lib_checkfunc(L, 2);
|
||||
auxsort(L, 1, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_PUSH("n")
|
||||
LJLIB_CF(table_pack)
|
||||
{
|
||||
TValue *array, *base = L->base;
|
||||
MSize i, n = (uint32_t)(L->top - base);
|
||||
GCtab *t = lj_tab_new(L, n ? n+1 : 0, 1);
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
setintV(lj_tab_setstr(L, t, strV(lj_lib_upvalue(L, 1))), (int32_t)n);
|
||||
for (array = tvref(t->array) + 1, i = 0; i < n; i++)
|
||||
copyTV(L, &array[i], &base[i]);
|
||||
settabV(L, base, t);
|
||||
L->top = base+1;
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_table(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_TABLIBNAME, table);
|
||||
#if LJ_52
|
||||
lua_getglobal(L, "unpack");
|
||||
lua_setfield(L, -2, "unpack");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Valgrind suppression file for LuaJIT 2.0.
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr4
|
||||
fun:lj_str_cmp
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr1
|
||||
fun:lj_str_cmp
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr4
|
||||
fun:lj_str_new
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr1
|
||||
fun:lj_str_new
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Cond
|
||||
fun:lj_str_new
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
** Bundled memory allocator.
|
||||
** Donated to the public domain.
|
||||
*/
|
||||
|
||||
#ifndef _LJ_ALLOC_H
|
||||
#define _LJ_ALLOC_H
|
||||
|
||||
#include "lj_def.h"
|
||||
|
||||
#ifndef LUAJIT_USE_SYSMALLOC
|
||||
LJ_FUNC void *lj_alloc_create(void);
|
||||
LJ_FUNC void lj_alloc_destroy(void *msp);
|
||||
LJ_FUNC void *lj_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
** Target architecture selection.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_ARCH_H
|
||||
#define _LJ_ARCH_H
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
/* Target endianess. */
|
||||
#define LUAJIT_LE 0
|
||||
#define LUAJIT_BE 1
|
||||
|
||||
/* Target architectures. */
|
||||
#define LUAJIT_ARCH_X86 1
|
||||
#define LUAJIT_ARCH_x86 1
|
||||
#define LUAJIT_ARCH_X64 2
|
||||
#define LUAJIT_ARCH_x64 2
|
||||
#define LUAJIT_ARCH_ARM 3
|
||||
#define LUAJIT_ARCH_arm 3
|
||||
#define LUAJIT_ARCH_PPC 4
|
||||
#define LUAJIT_ARCH_ppc 4
|
||||
#define LUAJIT_ARCH_PPCSPE 5
|
||||
#define LUAJIT_ARCH_ppcspe 5
|
||||
#define LUAJIT_ARCH_MIPS 6
|
||||
#define LUAJIT_ARCH_mips 6
|
||||
|
||||
/* Target OS. */
|
||||
#define LUAJIT_OS_OTHER 0
|
||||
#define LUAJIT_OS_WINDOWS 1
|
||||
#define LUAJIT_OS_LINUX 2
|
||||
#define LUAJIT_OS_OSX 3
|
||||
#define LUAJIT_OS_BSD 4
|
||||
#define LUAJIT_OS_POSIX 5
|
||||
|
||||
/* Select native target if no target defined. */
|
||||
#ifndef LUAJIT_TARGET
|
||||
|
||||
#if defined(__i386) || defined(__i386__) || defined(_M_IX86)
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_X86
|
||||
#elif defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_X64
|
||||
#elif defined(__arm__) || defined(__arm) || defined(__ARM__) || defined(__ARM)
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_ARM
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__PPC__) || defined(__PPC) || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) || defined(__POWERPC) || defined(_M_PPC)
|
||||
#ifdef __NO_FPRS__
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_PPCSPE
|
||||
#else
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_PPC
|
||||
#endif
|
||||
#elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || defined(__MIPS)
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_MIPS
|
||||
#else
|
||||
#error "No support for this architecture (yet)"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Select native OS if no target OS defined. */
|
||||
#ifndef LUAJIT_OS
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX_VER)
|
||||
#define LUAJIT_OS LUAJIT_OS_WINDOWS
|
||||
#elif defined(__linux__)
|
||||
#define LUAJIT_OS LUAJIT_OS_LINUX
|
||||
#elif defined(__MACH__) && defined(__APPLE__)
|
||||
#define LUAJIT_OS LUAJIT_OS_OSX
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define LUAJIT_OS LUAJIT_OS_BSD
|
||||
#elif (defined(__sun__) && defined(__svr4__)) || defined(__CYGWIN__)
|
||||
#define LUAJIT_OS LUAJIT_OS_POSIX
|
||||
#else
|
||||
#define LUAJIT_OS LUAJIT_OS_OTHER
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Set target OS properties. */
|
||||
#if LUAJIT_OS == LUAJIT_OS_WINDOWS
|
||||
#define LJ_OS_NAME "Windows"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_LINUX
|
||||
#define LJ_OS_NAME "Linux"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_OSX
|
||||
#define LJ_OS_NAME "OSX"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_BSD
|
||||
#define LJ_OS_NAME "BSD"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_POSIX
|
||||
#define LJ_OS_NAME "POSIX"
|
||||
#else
|
||||
#define LJ_OS_NAME "Other"
|
||||
#endif
|
||||
|
||||
#define LJ_TARGET_WINDOWS (LUAJIT_OS == LUAJIT_OS_WINDOWS)
|
||||
#define LJ_TARGET_LINUX (LUAJIT_OS == LUAJIT_OS_LINUX)
|
||||
#define LJ_TARGET_OSX (LUAJIT_OS == LUAJIT_OS_OSX)
|
||||
#define LJ_TARGET_IOS (LJ_TARGET_OSX && LUAJIT_TARGET == LUAJIT_ARCH_ARM)
|
||||
#define LJ_TARGET_POSIX (LUAJIT_OS > LUAJIT_OS_WINDOWS)
|
||||
#define LJ_TARGET_DLOPEN LJ_TARGET_POSIX
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
#define LJ_TARGET_PS3 1
|
||||
#define LJ_TARGET_CONSOLE 1
|
||||
#endif
|
||||
|
||||
#if _XBOX_VER >= 200
|
||||
#define LJ_TARGET_XBOX360 1
|
||||
#define LJ_TARGET_CONSOLE 1
|
||||
#endif
|
||||
|
||||
#define LJ_NUMMODE_SINGLE 0 /* Single-number mode only. */
|
||||
#define LJ_NUMMODE_SINGLE_DUAL 1 /* Default to single-number mode. */
|
||||
#define LJ_NUMMODE_DUAL 2 /* Dual-number mode only. */
|
||||
#define LJ_NUMMODE_DUAL_SINGLE 3 /* Default to dual-number mode. */
|
||||
|
||||
/* Set target architecture properties. */
|
||||
#if LUAJIT_TARGET == LUAJIT_ARCH_X86
|
||||
|
||||
#define LJ_ARCH_NAME "x86"
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#if LJ_TARGET_WINDOWS || __CYGWIN__
|
||||
#define LJ_ABI_WIN 1
|
||||
#else
|
||||
#define LJ_ABI_WIN 0
|
||||
#endif
|
||||
#define LJ_TARGET_X86 1
|
||||
#define LJ_TARGET_X86ORX64 1
|
||||
#define LJ_TARGET_EHRETREG 0
|
||||
#define LJ_TARGET_MASKSHIFT 1
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNALIGNED 1
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_X64
|
||||
|
||||
#define LJ_ARCH_NAME "x64"
|
||||
#define LJ_ARCH_BITS 64
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#define LJ_ABI_WIN LJ_TARGET_WINDOWS
|
||||
#define LJ_TARGET_X64 1
|
||||
#define LJ_TARGET_X86ORX64 1
|
||||
#define LJ_TARGET_EHRETREG 0
|
||||
#define LJ_TARGET_JUMPRANGE 31 /* +-2^31 = +-2GB */
|
||||
#define LJ_TARGET_MASKSHIFT 1
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNALIGNED 1
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_ARM
|
||||
|
||||
#define LJ_ARCH_NAME "arm"
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#if !defined(LJ_ARCH_HASFPU) && __SOFTFP__
|
||||
#define LJ_ARCH_HASFPU 0
|
||||
#endif
|
||||
#if !defined(LJ_ABI_SOFTFP) && !__ARM_PCS_VFP
|
||||
#define LJ_ABI_SOFTFP 1
|
||||
#endif
|
||||
#define LJ_ABI_EABI 1
|
||||
#define LJ_TARGET_ARM 1
|
||||
#define LJ_TARGET_EHRETREG 0
|
||||
#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */
|
||||
#define LJ_TARGET_MASKSHIFT 0
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL
|
||||
|
||||
#if __ARM_ARCH_7__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH_7S__
|
||||
#define LJ_ARCH_VERSION 70
|
||||
#elif __ARM_ARCH_6T2__
|
||||
#define LJ_ARCH_VERSION 61
|
||||
#elif __ARM_ARCH_6__ || __ARM_ARCH_6J__ || __ARM_ARCH_6K__ || __ARM_ARCH_6Z__ || __ARM_ARCH_6ZK__
|
||||
#define LJ_ARCH_VERSION 60
|
||||
#else
|
||||
#define LJ_ARCH_VERSION 50
|
||||
#endif
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_PPC
|
||||
|
||||
#define LJ_ARCH_NAME "ppc"
|
||||
#if _LP64
|
||||
#define LJ_ARCH_BITS 64
|
||||
#else
|
||||
#define LJ_ARCH_BITS 32
|
||||
#endif
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_BE
|
||||
#define LJ_TARGET_PPC 1
|
||||
#define LJ_TARGET_EHRETREG 3
|
||||
#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */
|
||||
#define LJ_TARGET_MASKSHIFT 0
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 1 /* Want only IR_BROL. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL_SINGLE
|
||||
|
||||
#if _ARCH_PWR7
|
||||
#define LJ_ARCH_VERSION 70
|
||||
#elif _ARCH_PWR6
|
||||
#define LJ_ARCH_VERSION 60
|
||||
#elif _ARCH_PWR5X
|
||||
#define LJ_ARCH_VERSION 51
|
||||
#elif _ARCH_PWR5
|
||||
#define LJ_ARCH_VERSION 50
|
||||
#elif _ARCH_PWR4
|
||||
#define LJ_ARCH_VERSION 40
|
||||
#else
|
||||
#define LJ_ARCH_VERSION 0
|
||||
#endif
|
||||
#if __PPC64__ || __powerpc64__ || LJ_TARGET_CONSOLE
|
||||
#define LJ_ARCH_PPC64 1
|
||||
#define LJ_ARCH_NOFFI 1
|
||||
#endif
|
||||
#if _ARCH_PPCSQ
|
||||
#define LJ_ARCH_SQRT 1
|
||||
#endif
|
||||
#if _ARCH_PWR5X
|
||||
#define LJ_ARCH_ROUND 1
|
||||
#endif
|
||||
#if __PPU__
|
||||
#define LJ_ARCH_CELL 1
|
||||
#endif
|
||||
#if LJ_TARGET_XBOX360
|
||||
#define LJ_ARCH_XENON 1
|
||||
#endif
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_PPCSPE
|
||||
|
||||
#define LJ_ARCH_NAME "ppcspe"
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_BE
|
||||
#ifndef LJ_ABI_SOFTFP
|
||||
#define LJ_ABI_SOFTFP 1
|
||||
#endif
|
||||
#define LJ_ABI_EABI 1
|
||||
#define LJ_TARGET_PPCSPE 1
|
||||
#define LJ_TARGET_EHRETREG 3
|
||||
#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */
|
||||
#define LJ_TARGET_MASKSHIFT 0
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 1 /* Want only IR_BROL. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE
|
||||
#define LJ_ARCH_NOFFI 1 /* NYI: comparisons, calls. */
|
||||
#define LJ_ARCH_NOJIT 1
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_MIPS
|
||||
|
||||
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL)
|
||||
#define LJ_ARCH_NAME "mipsel"
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#else
|
||||
#define LJ_ARCH_NAME "mips"
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_BE
|
||||
#endif
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_TARGET_MIPS 1
|
||||
#define LJ_TARGET_EHRETREG 4
|
||||
#define LJ_TARGET_JUMPRANGE 27 /* 2*2^27 = 256MB-aligned region */
|
||||
#define LJ_TARGET_MASKSHIFT 1
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE
|
||||
|
||||
#if _MIPS_ARCH_MIPS32R2
|
||||
#define LJ_ARCH_VERSION 20
|
||||
#else
|
||||
#define LJ_ARCH_VERSION 10
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "No target architecture defined"
|
||||
#endif
|
||||
|
||||
#ifndef LJ_PAGESIZE
|
||||
#define LJ_PAGESIZE 4096
|
||||
#endif
|
||||
|
||||
/* Check for minimum required compiler versions. */
|
||||
#if defined(__GNUC__)
|
||||
#if LJ_TARGET_X86
|
||||
#if (__GNUC__ < 3) || ((__GNUC__ == 3) && __GNUC_MINOR__ < 4)
|
||||
#error "Need at least GCC 3.4 or newer"
|
||||
#endif
|
||||
#elif LJ_TARGET_X64
|
||||
#if __GNUC__ < 4
|
||||
#error "Need at least GCC 4.0 or newer"
|
||||
#endif
|
||||
#elif LJ_TARGET_ARM
|
||||
#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 2)
|
||||
#error "Need at least GCC 4.2 or newer"
|
||||
#endif
|
||||
#elif !LJ_TARGET_PS3
|
||||
#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 3)
|
||||
#error "Need at least GCC 4.3 or newer"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Check target-specific constraints. */
|
||||
#ifndef _BUILDVM_H
|
||||
#if LJ_TARGET_X64
|
||||
#if __USING_SJLJ_EXCEPTIONS__
|
||||
#error "Need a C compiler with native exception handling on x64"
|
||||
#endif
|
||||
#elif LJ_TARGET_ARM
|
||||
#if defined(__ARMEB__)
|
||||
#error "No support for big-endian ARM"
|
||||
#endif
|
||||
#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__
|
||||
#error "No support for Cortex-M CPUs"
|
||||
#endif
|
||||
#if !(__ARM_EABI__ || LJ_TARGET_IOS)
|
||||
#error "Only ARM EABI or iOS 3.0+ ABI is supported"
|
||||
#endif
|
||||
#elif LJ_TARGET_PPC || LJ_TARGET_PPCSPE
|
||||
#if defined(_SOFT_FLOAT) || defined(_SOFT_DOUBLE)
|
||||
#error "No support for PowerPC CPUs without double-precision FPU"
|
||||
#endif
|
||||
#if defined(_LITTLE_ENDIAN)
|
||||
#error "No support for little-endian PowerPC"
|
||||
#endif
|
||||
#if defined(_LP64)
|
||||
#error "No support for PowerPC 64 bit mode"
|
||||
#endif
|
||||
#elif LJ_TARGET_MIPS
|
||||
#if defined(__mips_soft_float)
|
||||
#error "No support for MIPS CPUs without FPU"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Enable or disable the dual-number mode for the VM. */
|
||||
#if (LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE && LUAJIT_NUMMODE == 2) || \
|
||||
(LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL && LUAJIT_NUMMODE == 1)
|
||||
#error "No support for this number mode on this architecture"
|
||||
#endif
|
||||
#if LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL || \
|
||||
(LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL_SINGLE && LUAJIT_NUMMODE != 1) || \
|
||||
(LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE_DUAL && LUAJIT_NUMMODE == 2)
|
||||
#define LJ_DUALNUM 1
|
||||
#else
|
||||
#define LJ_DUALNUM 0
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_IOS || LJ_TARGET_CONSOLE
|
||||
/* Runtime code generation is restricted on iOS. Complain to Apple, not me. */
|
||||
/* Ditto for the consoles. Complain to Sony or MS, not me. */
|
||||
#ifndef LUAJIT_ENABLE_JIT
|
||||
#define LJ_OS_NOJIT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Disable or enable the JIT compiler. */
|
||||
#if defined(LUAJIT_DISABLE_JIT) || defined(LJ_ARCH_NOJIT) || defined(LJ_OS_NOJIT)
|
||||
#define LJ_HASJIT 0
|
||||
#else
|
||||
#define LJ_HASJIT 1
|
||||
#endif
|
||||
|
||||
/* Disable or enable the FFI extension. */
|
||||
#if defined(LUAJIT_DISABLE_FFI) || defined(LJ_ARCH_NOFFI)
|
||||
#define LJ_HASFFI 0
|
||||
#else
|
||||
#define LJ_HASFFI 1
|
||||
#endif
|
||||
|
||||
#ifndef LJ_ARCH_HASFPU
|
||||
#define LJ_ARCH_HASFPU 1
|
||||
#endif
|
||||
#ifndef LJ_ABI_SOFTFP
|
||||
#define LJ_ABI_SOFTFP 0
|
||||
#endif
|
||||
#define LJ_SOFTFP (!LJ_ARCH_HASFPU)
|
||||
|
||||
#if LJ_ARCH_ENDIAN == LUAJIT_BE
|
||||
#define LJ_LE 0
|
||||
#define LJ_BE 1
|
||||
#define LJ_ENDIAN_SELECT(le, be) be
|
||||
#define LJ_ENDIAN_LOHI(lo, hi) hi lo
|
||||
#else
|
||||
#define LJ_LE 1
|
||||
#define LJ_BE 0
|
||||
#define LJ_ENDIAN_SELECT(le, be) le
|
||||
#define LJ_ENDIAN_LOHI(lo, hi) lo hi
|
||||
#endif
|
||||
|
||||
#if LJ_ARCH_BITS == 32
|
||||
#define LJ_32 1
|
||||
#define LJ_64 0
|
||||
#else
|
||||
#define LJ_32 0
|
||||
#define LJ_64 1
|
||||
#endif
|
||||
|
||||
#ifndef LJ_TARGET_UNALIGNED
|
||||
#define LJ_TARGET_UNALIGNED 0
|
||||
#endif
|
||||
|
||||
/* Various workarounds for embedded operating systems. */
|
||||
#if (defined(__ANDROID__) && !defined(LJ_TARGET_X86ORX64)) || defined(__symbian__) || LJ_TARGET_XBOX360
|
||||
#define LUAJIT_NO_LOG2
|
||||
#endif
|
||||
#if defined(__symbian__)
|
||||
#define LUAJIT_NO_EXP2
|
||||
#endif
|
||||
|
||||
#if defined(LUAJIT_NO_UNWIND) || defined(__symbian__) || LJ_TARGET_IOS || LJ_TARGET_PS3
|
||||
#define LJ_NO_UNWIND 1
|
||||
#endif
|
||||
|
||||
/* Compatibility with Lua 5.1 vs. 5.2. */
|
||||
#ifdef LUAJIT_ENABLE_LUA52COMPAT
|
||||
#define LJ_52 1
|
||||
#else
|
||||
#define LJ_52 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
** Bytecode writer.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_bcwrite_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#endif
|
||||
#if LJ_HASJIT
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_jit.h"
|
||||
#endif
|
||||
#include "lj_bcdump.h"
|
||||
#include "lj_vm.h"
|
||||
|
||||
/* Context for bytecode writer. */
|
||||
typedef struct BCWriteCtx {
|
||||
SBuf sb; /* Output buffer. */
|
||||
lua_State *L; /* Lua state. */
|
||||
GCproto *pt; /* Root prototype. */
|
||||
lua_Writer wfunc; /* Writer callback. */
|
||||
void *wdata; /* Writer callback data. */
|
||||
int strip; /* Strip debug info. */
|
||||
int status; /* Status from writer callback. */
|
||||
} BCWriteCtx;
|
||||
|
||||
/* -- Output buffer handling ---------------------------------------------- */
|
||||
|
||||
/* Resize buffer if needed. */
|
||||
static LJ_NOINLINE void bcwrite_resize(BCWriteCtx *ctx, MSize len)
|
||||
{
|
||||
MSize sz = ctx->sb.sz * 2;
|
||||
while (ctx->sb.n + len > sz) sz = sz * 2;
|
||||
lj_str_resizebuf(ctx->L, &ctx->sb, sz);
|
||||
}
|
||||
|
||||
/* Need a certain amount of buffer space. */
|
||||
static LJ_AINLINE void bcwrite_need(BCWriteCtx *ctx, MSize len)
|
||||
{
|
||||
if (LJ_UNLIKELY(ctx->sb.n + len > ctx->sb.sz))
|
||||
bcwrite_resize(ctx, len);
|
||||
}
|
||||
|
||||
/* Add memory block to buffer. */
|
||||
static void bcwrite_block(BCWriteCtx *ctx, const void *p, MSize len)
|
||||
{
|
||||
uint8_t *q = (uint8_t *)(ctx->sb.buf + ctx->sb.n);
|
||||
MSize i;
|
||||
ctx->sb.n += len;
|
||||
for (i = 0; i < len; i++) q[i] = ((uint8_t *)p)[i];
|
||||
}
|
||||
|
||||
/* Add byte to buffer. */
|
||||
static LJ_AINLINE void bcwrite_byte(BCWriteCtx *ctx, uint8_t b)
|
||||
{
|
||||
ctx->sb.buf[ctx->sb.n++] = b;
|
||||
}
|
||||
|
||||
/* Add ULEB128 value to buffer. */
|
||||
static void bcwrite_uleb128(BCWriteCtx *ctx, uint32_t v)
|
||||
{
|
||||
MSize n = ctx->sb.n;
|
||||
uint8_t *p = (uint8_t *)ctx->sb.buf;
|
||||
for (; v >= 0x80; v >>= 7)
|
||||
p[n++] = (uint8_t)((v & 0x7f) | 0x80);
|
||||
p[n++] = (uint8_t)v;
|
||||
ctx->sb.n = n;
|
||||
}
|
||||
|
||||
/* -- Bytecode writer ----------------------------------------------------- */
|
||||
|
||||
/* Write a single constant key/value of a template table. */
|
||||
static void bcwrite_ktabk(BCWriteCtx *ctx, cTValue *o, int narrow)
|
||||
{
|
||||
bcwrite_need(ctx, 1+10);
|
||||
if (tvisstr(o)) {
|
||||
const GCstr *str = strV(o);
|
||||
MSize len = str->len;
|
||||
bcwrite_need(ctx, 5+len);
|
||||
bcwrite_uleb128(ctx, BCDUMP_KTAB_STR+len);
|
||||
bcwrite_block(ctx, strdata(str), len);
|
||||
} else if (tvisint(o)) {
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_INT);
|
||||
bcwrite_uleb128(ctx, intV(o));
|
||||
} else if (tvisnum(o)) {
|
||||
if (!LJ_DUALNUM && narrow) { /* Narrow number constants to integers. */
|
||||
lua_Number num = numV(o);
|
||||
int32_t k = lj_num2int(num);
|
||||
if (num == (lua_Number)k) { /* -0 is never a constant. */
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_INT);
|
||||
bcwrite_uleb128(ctx, k);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_NUM);
|
||||
bcwrite_uleb128(ctx, o->u32.lo);
|
||||
bcwrite_uleb128(ctx, o->u32.hi);
|
||||
} else {
|
||||
lua_assert(tvispri(o));
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_NIL+~itype(o));
|
||||
}
|
||||
}
|
||||
|
||||
/* Write a template table. */
|
||||
static void bcwrite_ktab(BCWriteCtx *ctx, const GCtab *t)
|
||||
{
|
||||
MSize narray = 0, nhash = 0;
|
||||
if (t->asize > 0) { /* Determine max. length of array part. */
|
||||
ptrdiff_t i;
|
||||
TValue *array = tvref(t->array);
|
||||
for (i = (ptrdiff_t)t->asize-1; i >= 0; i--)
|
||||
if (!tvisnil(&array[i]))
|
||||
break;
|
||||
narray = (MSize)(i+1);
|
||||
}
|
||||
if (t->hmask > 0) { /* Count number of used hash slots. */
|
||||
MSize i, hmask = t->hmask;
|
||||
Node *node = noderef(t->node);
|
||||
for (i = 0; i <= hmask; i++)
|
||||
nhash += !tvisnil(&node[i].val);
|
||||
}
|
||||
/* Write number of array slots and hash slots. */
|
||||
bcwrite_uleb128(ctx, narray);
|
||||
bcwrite_uleb128(ctx, nhash);
|
||||
if (narray) { /* Write array entries (may contain nil). */
|
||||
MSize i;
|
||||
TValue *o = tvref(t->array);
|
||||
for (i = 0; i < narray; i++, o++)
|
||||
bcwrite_ktabk(ctx, o, 1);
|
||||
}
|
||||
if (nhash) { /* Write hash entries. */
|
||||
MSize i = nhash;
|
||||
Node *node = noderef(t->node) + t->hmask;
|
||||
for (;; node--)
|
||||
if (!tvisnil(&node->val)) {
|
||||
bcwrite_ktabk(ctx, &node->key, 0);
|
||||
bcwrite_ktabk(ctx, &node->val, 1);
|
||||
if (--i == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write GC constants of a prototype. */
|
||||
static void bcwrite_kgc(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize i, sizekgc = pt->sizekgc;
|
||||
GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc;
|
||||
for (i = 0; i < sizekgc; i++, kr++) {
|
||||
GCobj *o = gcref(*kr);
|
||||
MSize tp, need = 1;
|
||||
/* Determine constant type and needed size. */
|
||||
if (o->gch.gct == ~LJ_TSTR) {
|
||||
tp = BCDUMP_KGC_STR + gco2str(o)->len;
|
||||
need = 5+gco2str(o)->len;
|
||||
} else if (o->gch.gct == ~LJ_TPROTO) {
|
||||
lua_assert((pt->flags & PROTO_CHILD));
|
||||
tp = BCDUMP_KGC_CHILD;
|
||||
#if LJ_HASFFI
|
||||
} else if (o->gch.gct == ~LJ_TCDATA) {
|
||||
CTypeID id = gco2cd(o)->ctypeid;
|
||||
need = 1+4*5;
|
||||
if (id == CTID_INT64) {
|
||||
tp = BCDUMP_KGC_I64;
|
||||
} else if (id == CTID_UINT64) {
|
||||
tp = BCDUMP_KGC_U64;
|
||||
} else {
|
||||
lua_assert(id == CTID_COMPLEX_DOUBLE);
|
||||
tp = BCDUMP_KGC_COMPLEX;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
lua_assert(o->gch.gct == ~LJ_TTAB);
|
||||
tp = BCDUMP_KGC_TAB;
|
||||
need = 1+2*5;
|
||||
}
|
||||
/* Write constant type. */
|
||||
bcwrite_need(ctx, need);
|
||||
bcwrite_uleb128(ctx, tp);
|
||||
/* Write constant data (if any). */
|
||||
if (tp >= BCDUMP_KGC_STR) {
|
||||
bcwrite_block(ctx, strdata(gco2str(o)), gco2str(o)->len);
|
||||
} else if (tp == BCDUMP_KGC_TAB) {
|
||||
bcwrite_ktab(ctx, gco2tab(o));
|
||||
#if LJ_HASFFI
|
||||
} else if (tp != BCDUMP_KGC_CHILD) {
|
||||
cTValue *p = (TValue *)cdataptr(gco2cd(o));
|
||||
bcwrite_uleb128(ctx, p[0].u32.lo);
|
||||
bcwrite_uleb128(ctx, p[0].u32.hi);
|
||||
if (tp == BCDUMP_KGC_COMPLEX) {
|
||||
bcwrite_uleb128(ctx, p[1].u32.lo);
|
||||
bcwrite_uleb128(ctx, p[1].u32.hi);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write number constants of a prototype. */
|
||||
static void bcwrite_knum(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize i, sizekn = pt->sizekn;
|
||||
cTValue *o = mref(pt->k, TValue);
|
||||
bcwrite_need(ctx, 10*sizekn);
|
||||
for (i = 0; i < sizekn; i++, o++) {
|
||||
int32_t k;
|
||||
if (tvisint(o)) {
|
||||
k = intV(o);
|
||||
goto save_int;
|
||||
} else {
|
||||
/* Write a 33 bit ULEB128 for the int (lsb=0) or loword (lsb=1). */
|
||||
if (!LJ_DUALNUM) { /* Narrow number constants to integers. */
|
||||
lua_Number num = numV(o);
|
||||
k = lj_num2int(num);
|
||||
if (num == (lua_Number)k) { /* -0 is never a constant. */
|
||||
save_int:
|
||||
bcwrite_uleb128(ctx, 2*(uint32_t)k | ((uint32_t)k & 0x80000000u));
|
||||
if (k < 0) {
|
||||
char *p = &ctx->sb.buf[ctx->sb.n-1];
|
||||
*p = (*p & 7) | ((k>>27) & 0x18);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bcwrite_uleb128(ctx, 1+(2*o->u32.lo | (o->u32.lo & 0x80000000u)));
|
||||
if (o->u32.lo >= 0x80000000u) {
|
||||
char *p = &ctx->sb.buf[ctx->sb.n-1];
|
||||
*p = (*p & 7) | ((o->u32.lo>>27) & 0x18);
|
||||
}
|
||||
bcwrite_uleb128(ctx, o->u32.hi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write bytecode instructions. */
|
||||
static void bcwrite_bytecode(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize nbc = pt->sizebc-1; /* Omit the [JI]FUNC* header. */
|
||||
#if LJ_HASJIT
|
||||
uint8_t *p = (uint8_t *)&ctx->sb.buf[ctx->sb.n];
|
||||
#endif
|
||||
bcwrite_block(ctx, proto_bc(pt)+1, nbc*(MSize)sizeof(BCIns));
|
||||
#if LJ_HASJIT
|
||||
/* Unpatch modified bytecode containing ILOOP/JLOOP etc. */
|
||||
if ((pt->flags & PROTO_ILOOP) || pt->trace) {
|
||||
jit_State *J = L2J(ctx->L);
|
||||
MSize i;
|
||||
for (i = 0; i < nbc; i++, p += sizeof(BCIns)) {
|
||||
BCOp op = (BCOp)p[LJ_ENDIAN_SELECT(0, 3)];
|
||||
if (op == BC_IFORL || op == BC_IITERL || op == BC_ILOOP ||
|
||||
op == BC_JFORI) {
|
||||
p[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_IFORL+BC_FORL);
|
||||
} else if (op == BC_JFORL || op == BC_JITERL || op == BC_JLOOP) {
|
||||
BCReg rd = p[LJ_ENDIAN_SELECT(2, 1)] + (p[LJ_ENDIAN_SELECT(3, 0)] << 8);
|
||||
BCIns ins = traceref(J, rd)->startins;
|
||||
p[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_JFORL+BC_FORL);
|
||||
p[LJ_ENDIAN_SELECT(2, 1)] = bc_c(ins);
|
||||
p[LJ_ENDIAN_SELECT(3, 0)] = bc_b(ins);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Write prototype. */
|
||||
static void bcwrite_proto(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize sizedbg = 0;
|
||||
|
||||
/* Recursively write children of prototype. */
|
||||
if ((pt->flags & PROTO_CHILD)) {
|
||||
ptrdiff_t i, n = pt->sizekgc;
|
||||
GCRef *kr = mref(pt->k, GCRef) - 1;
|
||||
for (i = 0; i < n; i++, kr--) {
|
||||
GCobj *o = gcref(*kr);
|
||||
if (o->gch.gct == ~LJ_TPROTO)
|
||||
bcwrite_proto(ctx, gco2pt(o));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start writing the prototype info to a buffer. */
|
||||
lj_str_resetbuf(&ctx->sb);
|
||||
ctx->sb.n = 5; /* Leave room for final size. */
|
||||
bcwrite_need(ctx, 4+6*5+(pt->sizebc-1)*(MSize)sizeof(BCIns)+pt->sizeuv*2);
|
||||
|
||||
/* Write prototype header. */
|
||||
bcwrite_byte(ctx, (pt->flags & (PROTO_CHILD|PROTO_VARARG|PROTO_FFI)));
|
||||
bcwrite_byte(ctx, pt->numparams);
|
||||
bcwrite_byte(ctx, pt->framesize);
|
||||
bcwrite_byte(ctx, pt->sizeuv);
|
||||
bcwrite_uleb128(ctx, pt->sizekgc);
|
||||
bcwrite_uleb128(ctx, pt->sizekn);
|
||||
bcwrite_uleb128(ctx, pt->sizebc-1);
|
||||
if (!ctx->strip) {
|
||||
if (proto_lineinfo(pt))
|
||||
sizedbg = pt->sizept - (MSize)((char *)proto_lineinfo(pt) - (char *)pt);
|
||||
bcwrite_uleb128(ctx, sizedbg);
|
||||
if (sizedbg) {
|
||||
bcwrite_uleb128(ctx, pt->firstline);
|
||||
bcwrite_uleb128(ctx, pt->numline);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write bytecode instructions and upvalue refs. */
|
||||
bcwrite_bytecode(ctx, pt);
|
||||
bcwrite_block(ctx, proto_uv(pt), pt->sizeuv*2);
|
||||
|
||||
/* Write constants. */
|
||||
bcwrite_kgc(ctx, pt);
|
||||
bcwrite_knum(ctx, pt);
|
||||
|
||||
/* Write debug info, if not stripped. */
|
||||
if (sizedbg) {
|
||||
bcwrite_need(ctx, sizedbg);
|
||||
bcwrite_block(ctx, proto_lineinfo(pt), sizedbg);
|
||||
}
|
||||
|
||||
/* Pass buffer to writer function. */
|
||||
if (ctx->status == 0) {
|
||||
MSize n = ctx->sb.n - 5;
|
||||
MSize nn = (lj_fls(n)+8)*9 >> 6;
|
||||
ctx->sb.n = 5 - nn;
|
||||
bcwrite_uleb128(ctx, n); /* Fill in final size. */
|
||||
lua_assert(ctx->sb.n == 5);
|
||||
ctx->status = ctx->wfunc(ctx->L, ctx->sb.buf+5-nn, nn+n, ctx->wdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write header of bytecode dump. */
|
||||
static void bcwrite_header(BCWriteCtx *ctx)
|
||||
{
|
||||
GCstr *chunkname = proto_chunkname(ctx->pt);
|
||||
const char *name = strdata(chunkname);
|
||||
MSize len = chunkname->len;
|
||||
lj_str_resetbuf(&ctx->sb);
|
||||
bcwrite_need(ctx, 5+5+len);
|
||||
bcwrite_byte(ctx, BCDUMP_HEAD1);
|
||||
bcwrite_byte(ctx, BCDUMP_HEAD2);
|
||||
bcwrite_byte(ctx, BCDUMP_HEAD3);
|
||||
bcwrite_byte(ctx, BCDUMP_VERSION);
|
||||
bcwrite_byte(ctx, (ctx->strip ? BCDUMP_F_STRIP : 0) +
|
||||
(LJ_BE ? BCDUMP_F_BE : 0) +
|
||||
((ctx->pt->flags & PROTO_FFI) ? BCDUMP_F_FFI : 0));
|
||||
if (!ctx->strip) {
|
||||
bcwrite_uleb128(ctx, len);
|
||||
bcwrite_block(ctx, name, len);
|
||||
}
|
||||
ctx->status = ctx->wfunc(ctx->L, ctx->sb.buf, ctx->sb.n, ctx->wdata);
|
||||
}
|
||||
|
||||
/* Write footer of bytecode dump. */
|
||||
static void bcwrite_footer(BCWriteCtx *ctx)
|
||||
{
|
||||
if (ctx->status == 0) {
|
||||
uint8_t zero = 0;
|
||||
ctx->status = ctx->wfunc(ctx->L, &zero, 1, ctx->wdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Protected callback for bytecode writer. */
|
||||
static TValue *cpwriter(lua_State *L, lua_CFunction dummy, void *ud)
|
||||
{
|
||||
BCWriteCtx *ctx = (BCWriteCtx *)ud;
|
||||
UNUSED(dummy);
|
||||
lj_str_resizebuf(L, &ctx->sb, 1024); /* Avoids resize for most prototypes. */
|
||||
bcwrite_header(ctx);
|
||||
bcwrite_proto(ctx, ctx->pt);
|
||||
bcwrite_footer(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Write bytecode for a prototype. */
|
||||
int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer, void *data,
|
||||
int strip)
|
||||
{
|
||||
BCWriteCtx ctx;
|
||||
int status;
|
||||
ctx.L = L;
|
||||
ctx.pt = pt;
|
||||
ctx.wfunc = writer;
|
||||
ctx.wdata = data;
|
||||
ctx.strip = strip;
|
||||
ctx.status = 0;
|
||||
lj_str_initbuf(&ctx.sb);
|
||||
status = lj_vm_cpcall(L, NULL, &ctx, cpwriter);
|
||||
if (status == 0) status = ctx.status;
|
||||
lj_str_freebuf(G(ctx.L), &ctx.sb);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
** C data arithmetic.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CARITH_H
|
||||
#define _LJ_CARITH_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
LJ_FUNC int lj_carith_op(lua_State *L, MMS mm);
|
||||
|
||||
#if LJ_32 && LJ_HASJIT
|
||||
LJ_FUNC int64_t lj_carith_mul64(int64_t x, int64_t k);
|
||||
#endif
|
||||
LJ_FUNC uint64_t lj_carith_divu64(uint64_t a, uint64_t b);
|
||||
LJ_FUNC int64_t lj_carith_divi64(int64_t a, int64_t b);
|
||||
LJ_FUNC uint64_t lj_carith_modu64(uint64_t a, uint64_t b);
|
||||
LJ_FUNC int64_t lj_carith_modi64(int64_t a, int64_t b);
|
||||
LJ_FUNC uint64_t lj_carith_powu64(uint64_t x, uint64_t k);
|
||||
LJ_FUNC int64_t lj_carith_powi64(int64_t x, int64_t k);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,837 +0,0 @@
|
||||
/*
|
||||
** FFI C call handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_ccall.h"
|
||||
#include "lj_trace.h"
|
||||
|
||||
/* Target-specific handling of register arguments. */
|
||||
#if LJ_TARGET_X86
|
||||
/* -- x86 calling conventions --------------------------------------------- */
|
||||
|
||||
#if LJ_ABI_WIN
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
/* Return structs bigger than 8 by reference (on stack only). */ \
|
||||
cc->retref = (sz > 8); \
|
||||
if (cc->retref) cc->stack[nsp++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET CCALL_HANDLE_STRUCTRET
|
||||
|
||||
#else
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference (in reg or on stack). */ \
|
||||
if (ngpr < maxgpr) \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp; \
|
||||
else \
|
||||
cc->stack[nsp++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Return complex float in GPRs and complex double by reference. */ \
|
||||
cc->retref = (sz > 8); \
|
||||
if (cc->retref) { \
|
||||
if (ngpr < maxgpr) \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp; \
|
||||
else \
|
||||
cc->stack[nsp++] = (GPRArg)dp; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (!cc->retref) \
|
||||
*(int64_t *)dp = *(int64_t *)sp; /* Copy complex float from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
ngpr = maxgpr; /* Pass all structs by value on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
isfp = 1; /* Pass complex by value on stack. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (!isfp) { /* Only non-FP values may be passed in registers. */ \
|
||||
if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \
|
||||
if (!LJ_ABI_WIN) ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} else if (ngpr + 1 <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64 && LJ_ABI_WIN
|
||||
/* -- Windows/x64 calling conventions ------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
/* Return structs of size 1, 2, 4 or 8 in a GPR. */ \
|
||||
cc->retref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8); \
|
||||
if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET CCALL_HANDLE_STRUCTRET
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (!cc->retref) \
|
||||
*(int64_t *)dp = *(int64_t *)sp; /* Copy complex float from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
/* Pass structs of size 1, 2, 4 or 8 in a GPR by value. */ \
|
||||
if (!(sz == 1 || sz == 2 || sz == 4 || sz == 8)) { \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; /* Pass all other structs by reference. */ \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex float in a GPR and complex double by reference. */ \
|
||||
if (sz != 2*sizeof(float)) { \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; \
|
||||
}
|
||||
|
||||
/* Windows/x64 argument registers are strictly positional (use ngpr). */
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (ngpr < 4) { dp = &cc->fpr[ngpr++]; nfpr = ngpr; goto done; } \
|
||||
} else { \
|
||||
if (ngpr < 4) { dp = &cc->gpr[ngpr++]; goto done; } \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64
|
||||
/* -- POSIX/x64 calling conventions --------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
int rcl[2]; rcl[0] = rcl[1] = 0; \
|
||||
if (ccall_classify_struct(cts, ctr, rcl, 0)) { \
|
||||
cc->retref = 1; /* Return struct by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp; \
|
||||
} else { \
|
||||
cc->retref = 0; /* Return small structs in registers. */ \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET2 \
|
||||
int rcl[2]; rcl[0] = rcl[1] = 0; \
|
||||
ccall_classify_struct(cts, ctr, rcl, 0); \
|
||||
ccall_struct_ret(cc, rcl, dp, ctr->size);
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in one or two FPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPR. */ \
|
||||
*(int64_t *)dp = cc->fpr[0].l[0]; \
|
||||
} else { /* Copy non-contiguous complex double from FPRs. */ \
|
||||
((int64_t *)dp)[0] = cc->fpr[0].l[0]; \
|
||||
((int64_t *)dp)[1] = cc->fpr[1].l[0]; \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
int rcl[2]; rcl[0] = rcl[1] = 0; \
|
||||
if (!ccall_classify_struct(cts, d, rcl, 0)) { \
|
||||
cc->nsp = nsp; cc->ngpr = ngpr; cc->nfpr = nfpr; \
|
||||
if (ccall_struct_arg(cc, cts, d, rcl, o, narg)) goto err_nyi; \
|
||||
nsp = cc->nsp; ngpr = cc->ngpr; nfpr = cc->nfpr; \
|
||||
continue; \
|
||||
} /* Pass all other structs by value on stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
isfp = 2; /* Pass complex in FPRs or on stack. Needs postprocessing. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp) { /* Try to pass argument in FPRs. */ \
|
||||
if (nfpr + n <= CCALL_NARG_FPR) { \
|
||||
dp = &cc->fpr[nfpr]; \
|
||||
nfpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
/* Note that reordering is explicitly allowed in the x64 ABI. */ \
|
||||
if (n <= 2 && ngpr + n <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
/* -- ARM calling conventions --------------------------------------------- */
|
||||
|
||||
#if LJ_ABI_SOFTFP
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
/* Return structs of size <= 4 in a GPR. */ \
|
||||
cc->retref = !(sz <= 4); \
|
||||
if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
cc->retref = 1; /* Return all complex values by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
UNUSED(dp); /* Nothing to do. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
/* Pass all structs by value in registers and/or on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG_FP1
|
||||
#define CCALL_HANDLE_REGARG_FP2
|
||||
|
||||
#else
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = !ccall_classify_struct(cts, ctr, ct); \
|
||||
if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET2 \
|
||||
if (ccall_classify_struct(cts, ctr, ct) > 1) sp = (uint8_t *)&cc->fpr[0]; \
|
||||
memcpy(dp, sp, ctr->size);
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
if (!(ct->info & CTF_VARARG)) cc->retref = 0; /* Return complex in FPRs. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (!(ct->info & CTF_VARARG)) memcpy(dp, &cc->fpr[0], ctr->size);
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
isfp = (ccall_classify_struct(cts, d, ct) > 1);
|
||||
/* Pass all structs by value in registers and/or on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
isfp = 1; /* Pass complex by value in FPRs or on stack. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG_FP1 \
|
||||
if (isfp && !(ct->info & CTF_VARARG)) { \
|
||||
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \
|
||||
if (nfpr + (n >> 1) <= CCALL_NARG_FPR) { \
|
||||
dp = &cc->fpr[nfpr]; \
|
||||
nfpr += (n >> 1); \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (sz > 1 && fprodd != nfpr) fprodd = 0; \
|
||||
if (fprodd) { \
|
||||
if (2*nfpr+n <= 2*CCALL_NARG_FPR+1) { \
|
||||
dp = (void *)&cc->fpr[fprodd-1].f[1]; \
|
||||
nfpr += (n >> 1); \
|
||||
if ((n & 1)) fprodd = 0; else fprodd = nfpr-1; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (2*nfpr+n <= 2*CCALL_NARG_FPR) { \
|
||||
dp = (void *)&cc->fpr[nfpr]; \
|
||||
nfpr += (n >> 1); \
|
||||
if ((n & 1)) fprodd = ++nfpr; else fprodd = 0; \
|
||||
goto done; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
fprodd = 0; /* No reordering after the first FP value is on stack. */ \
|
||||
} else {
|
||||
|
||||
#define CCALL_HANDLE_REGARG_FP2 }
|
||||
|
||||
#endif
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
CCALL_HANDLE_REGARG_FP1 \
|
||||
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \
|
||||
if (ngpr < maxgpr) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
} \
|
||||
if (ngpr < maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
if (ngpr + n > maxgpr) { \
|
||||
nsp += ngpr + n - maxgpr; /* Assumes contiguous gpr/stack fields. */ \
|
||||
if (nsp > CCALL_MAXSTACK) goto err_nyi; /* Too many arguments. */ \
|
||||
ngpr = maxgpr; \
|
||||
} else { \
|
||||
ngpr += n; \
|
||||
} \
|
||||
goto done; \
|
||||
} CCALL_HANDLE_REGARG_FP2
|
||||
|
||||
#define CCALL_HANDLE_RET \
|
||||
if ((ct->info & CTF_VARARG)) sp = (uint8_t *)&cc->gpr[0];
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
/* -- PPC calling conventions --------------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in 2 or 4 GPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
memcpy(dp, sp, ctr->size); /* Copy complex from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; /* Pass all structs by reference. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp) { /* Try to pass argument in FPRs. */ \
|
||||
if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
dp = &cc->fpr[nfpr]; \
|
||||
nfpr += 1; \
|
||||
d = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \
|
||||
goto done; \
|
||||
} \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
if (n > 1) { \
|
||||
lua_assert(n == 2 || n == 4); /* int64_t or complex (float). */ \
|
||||
if (ctype_isinteger(d->info)) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align int64_t to regpair. */ \
|
||||
else if (ngpr + n > maxgpr) \
|
||||
ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
ctr = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */
|
||||
|
||||
#elif LJ_TARGET_PPCSPE
|
||||
/* -- PPC/SPE calling conventions ----------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in 2 or 4 GPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
memcpy(dp, sp, ctr->size); /* Copy complex from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; /* Pass all structs by reference. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
/* PPC/SPE has a softfp ABI. */
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (n > 1) { /* Doesn't fit in a single GPR? */ \
|
||||
lua_assert(n == 2 || n == 4); /* int64_t, double or complex (float). */ \
|
||||
if (n == 2) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Only align 64 bit value to regpair. */ \
|
||||
else if (ngpr + n > maxgpr) \
|
||||
ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
/* -- MIPS calling conventions -------------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in 1 or 2 FPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPRs. */ \
|
||||
((float *)dp)[0] = cc->fpr[0].f; \
|
||||
((float *)dp)[1] = cc->fpr[1].f; \
|
||||
} else { /* Copy complex double from FPRs. */ \
|
||||
((double *)dp)[0] = cc->fpr[0].d; \
|
||||
((double *)dp)[1] = cc->fpr[1].d; \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
/* Pass all structs by value in registers and/or on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp && nfpr < CCALL_NARG_FPR && !(ct->info & CTF_VARARG)) { \
|
||||
/* Try to pass argument in FPRs. */ \
|
||||
dp = n == 1 ? (void *)&cc->fpr[nfpr].f : (void *)&cc->fpr[nfpr].d; \
|
||||
nfpr++; ngpr += n; \
|
||||
goto done; \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
nfpr = CCALL_NARG_FPR; \
|
||||
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
if (ngpr < maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
if (ngpr + n > maxgpr) { \
|
||||
nsp += ngpr + n - maxgpr; /* Assumes contiguous gpr/stack fields. */ \
|
||||
if (nsp > CCALL_MAXSTACK) goto err_nyi; /* Too many arguments. */ \
|
||||
ngpr = maxgpr; \
|
||||
} else { \
|
||||
ngpr += n; \
|
||||
} \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
sp = (uint8_t *)&cc->fpr[0].f;
|
||||
|
||||
#else
|
||||
#error "Missing calling convention definitions for this architecture"
|
||||
#endif
|
||||
|
||||
#ifndef CCALL_HANDLE_STRUCTRET2
|
||||
#define CCALL_HANDLE_STRUCTRET2 \
|
||||
memcpy(dp, sp, ctr->size); /* Copy struct return value from GPRs. */
|
||||
#endif
|
||||
|
||||
/* -- x64 struct classification ------------------------------------------- */
|
||||
|
||||
#if LJ_TARGET_X64 && !LJ_ABI_WIN
|
||||
|
||||
/* Register classes for x64 struct classification. */
|
||||
#define CCALL_RCL_INT 1
|
||||
#define CCALL_RCL_SSE 2
|
||||
#define CCALL_RCL_MEM 4
|
||||
/* NYI: classify vectors. */
|
||||
|
||||
static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs);
|
||||
|
||||
/* Classify a C type. */
|
||||
static void ccall_classify_ct(CTState *cts, CType *ct, int *rcl, CTSize ofs)
|
||||
{
|
||||
if (ctype_isarray(ct->info)) {
|
||||
CType *cct = ctype_rawchild(cts, ct);
|
||||
CTSize eofs, esz = cct->size, asz = ct->size;
|
||||
for (eofs = 0; eofs < asz; eofs += esz)
|
||||
ccall_classify_ct(cts, cct, rcl, ofs+eofs);
|
||||
} else if (ctype_isstruct(ct->info)) {
|
||||
ccall_classify_struct(cts, ct, rcl, ofs);
|
||||
} else {
|
||||
int cl = ctype_isfp(ct->info) ? CCALL_RCL_SSE : CCALL_RCL_INT;
|
||||
lua_assert(ctype_hassize(ct->info));
|
||||
if ((ofs & (ct->size-1))) cl = CCALL_RCL_MEM; /* Unaligned. */
|
||||
rcl[(ofs >= 8)] |= cl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Recursively classify a struct based on its fields. */
|
||||
static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs)
|
||||
{
|
||||
if (ct->size > 16) return CCALL_RCL_MEM; /* Too big, gets memory class. */
|
||||
while (ct->sib) {
|
||||
CTSize fofs;
|
||||
ct = ctype_get(cts, ct->sib);
|
||||
fofs = ofs+ct->size;
|
||||
if (ctype_isfield(ct->info))
|
||||
ccall_classify_ct(cts, ctype_rawchild(cts, ct), rcl, fofs);
|
||||
else if (ctype_isbitfield(ct->info))
|
||||
rcl[(fofs >= 8)] |= CCALL_RCL_INT; /* NYI: unaligned bitfields? */
|
||||
else if (ctype_isxattrib(ct->info, CTA_SUBTYPE))
|
||||
ccall_classify_struct(cts, ctype_rawchild(cts, ct), rcl, fofs);
|
||||
}
|
||||
return ((rcl[0]|rcl[1]) & CCALL_RCL_MEM); /* Memory class? */
|
||||
}
|
||||
|
||||
/* Try to split up a small struct into registers. */
|
||||
static int ccall_struct_reg(CCallState *cc, GPRArg *dp, int *rcl)
|
||||
{
|
||||
MSize ngpr = cc->ngpr, nfpr = cc->nfpr;
|
||||
uint32_t i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
lua_assert(!(rcl[i] & CCALL_RCL_MEM));
|
||||
if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */
|
||||
if (ngpr >= CCALL_NARG_GPR) return 1; /* Register overflow. */
|
||||
cc->gpr[ngpr++] = dp[i];
|
||||
} else if ((rcl[i] & CCALL_RCL_SSE)) {
|
||||
if (nfpr >= CCALL_NARG_FPR) return 1; /* Register overflow. */
|
||||
cc->fpr[nfpr++].l[0] = dp[i];
|
||||
}
|
||||
}
|
||||
cc->ngpr = ngpr; cc->nfpr = nfpr;
|
||||
return 0; /* Ok. */
|
||||
}
|
||||
|
||||
/* Pass a small struct argument. */
|
||||
static int ccall_struct_arg(CCallState *cc, CTState *cts, CType *d, int *rcl,
|
||||
TValue *o, int narg)
|
||||
{
|
||||
GPRArg dp[2];
|
||||
dp[0] = dp[1] = 0;
|
||||
/* Convert to temp. struct. */
|
||||
lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, CCF_ARG(narg));
|
||||
if (ccall_struct_reg(cc, dp, rcl)) { /* Register overflow? Pass on stack. */
|
||||
MSize nsp = cc->nsp, n = rcl[1] ? 2 : 1;
|
||||
if (nsp + n > CCALL_MAXSTACK) return 1; /* Too many arguments. */
|
||||
cc->nsp = nsp + n;
|
||||
memcpy(&cc->stack[nsp], dp, n*CTSIZE_PTR);
|
||||
}
|
||||
return 0; /* Ok. */
|
||||
}
|
||||
|
||||
/* Combine returned small struct. */
|
||||
static void ccall_struct_ret(CCallState *cc, int *rcl, uint8_t *dp, CTSize sz)
|
||||
{
|
||||
GPRArg sp[2];
|
||||
MSize ngpr = 0, nfpr = 0;
|
||||
uint32_t i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */
|
||||
sp[i] = cc->gpr[ngpr++];
|
||||
} else if ((rcl[i] & CCALL_RCL_SSE)) {
|
||||
sp[i] = cc->fpr[nfpr++].l[0];
|
||||
}
|
||||
}
|
||||
memcpy(dp, sp, sz);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -- ARM hard-float ABI struct classification ---------------------------- */
|
||||
|
||||
#if LJ_TARGET_ARM && !LJ_ABI_SOFTFP
|
||||
|
||||
/* Classify a struct based on its fields. */
|
||||
static unsigned int ccall_classify_struct(CTState *cts, CType *ct, CType *ctf)
|
||||
{
|
||||
CTSize sz = ct->size;
|
||||
unsigned int r = 0, n = 0, isu = (ct->info & CTF_UNION);
|
||||
if ((ctf->info & CTF_VARARG)) goto noth;
|
||||
while (ct->sib) {
|
||||
ct = ctype_get(cts, ct->sib);
|
||||
if (ctype_isfield(ct->info)) {
|
||||
CType *sct = ctype_rawchild(cts, ct);
|
||||
if (ctype_isfp(sct->info)) {
|
||||
r |= sct->size;
|
||||
if (!isu) n++; else if (n == 0) n = 1;
|
||||
} else if (ctype_iscomplex(sct->info)) {
|
||||
r |= (sct->size >> 1);
|
||||
if (!isu) n += 2; else if (n < 2) n = 2;
|
||||
} else {
|
||||
goto noth;
|
||||
}
|
||||
} else if (ctype_isbitfield(ct->info)) {
|
||||
goto noth;
|
||||
} else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
|
||||
CType *sct = ctype_rawchild(cts, ct);
|
||||
if (sct->size > 0) {
|
||||
unsigned int s = ccall_classify_struct(cts, sct, ctf);
|
||||
if (s <= 1) goto noth;
|
||||
r |= (s & 255);
|
||||
if (!isu) n += (s >> 8); else if (n < (s >>8)) n = (s >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((r == 4 || r == 8) && n <= 4)
|
||||
return r + (n << 8);
|
||||
noth: /* Not a homogeneous float/double aggregate. */
|
||||
return (sz <= 4); /* Return structs of size <= 4 in a GPR. */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* -- Common C call handling ---------------------------------------------- */
|
||||
|
||||
/* Infer the destination CTypeID for a vararg argument. */
|
||||
CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o)
|
||||
{
|
||||
if (tvisnumber(o)) {
|
||||
return CTID_DOUBLE;
|
||||
} else if (tviscdata(o)) {
|
||||
CTypeID id = cdataV(o)->ctypeid;
|
||||
CType *s = ctype_get(cts, id);
|
||||
if (ctype_isrefarray(s->info)) {
|
||||
return lj_ctype_intern(cts,
|
||||
CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(s->info)), CTSIZE_PTR);
|
||||
} else if (ctype_isstruct(s->info) || ctype_isfunc(s->info)) {
|
||||
/* NYI: how to pass a struct by value in a vararg argument? */
|
||||
return lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR);
|
||||
} else if (ctype_isfp(s->info) && s->size == sizeof(float)) {
|
||||
return CTID_DOUBLE;
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
} else if (tvisstr(o)) {
|
||||
return CTID_P_CCHAR;
|
||||
} else if (tvisbool(o)) {
|
||||
return CTID_BOOL;
|
||||
} else {
|
||||
return CTID_P_VOID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup arguments for C call. */
|
||||
static int ccall_set_args(lua_State *L, CTState *cts, CType *ct,
|
||||
CCallState *cc)
|
||||
{
|
||||
int gcsteps = 0;
|
||||
TValue *o, *top = L->top;
|
||||
CTypeID fid;
|
||||
CType *ctr;
|
||||
MSize maxgpr, ngpr = 0, nsp = 0, narg;
|
||||
#if CCALL_NARG_FPR
|
||||
MSize nfpr = 0;
|
||||
#if LJ_TARGET_ARM
|
||||
MSize fprodd = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Clear unused regs to get some determinism in case of misdeclaration. */
|
||||
memset(cc->gpr, 0, sizeof(cc->gpr));
|
||||
#if CCALL_NUM_FPR
|
||||
memset(cc->fpr, 0, sizeof(cc->fpr));
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_X86
|
||||
/* x86 has several different calling conventions. */
|
||||
cc->resx87 = 0;
|
||||
switch (ctype_cconv(ct->info)) {
|
||||
case CTCC_FASTCALL: maxgpr = 2; break;
|
||||
case CTCC_THISCALL: maxgpr = 1; break;
|
||||
default: maxgpr = 0; break;
|
||||
}
|
||||
#else
|
||||
maxgpr = CCALL_NARG_GPR;
|
||||
#endif
|
||||
|
||||
/* Perform required setup for some result types. */
|
||||
ctr = ctype_rawchild(cts, ct);
|
||||
if (ctype_isvector(ctr->info)) {
|
||||
if (!(CCALL_VECTOR_REG && (ctr->size == 8 || ctr->size == 16)))
|
||||
goto err_nyi;
|
||||
} else if (ctype_iscomplex(ctr->info) || ctype_isstruct(ctr->info)) {
|
||||
/* Preallocate cdata object and anchor it after arguments. */
|
||||
CTSize sz = ctr->size;
|
||||
GCcdata *cd = lj_cdata_new(cts, ctype_cid(ct->info), sz);
|
||||
void *dp = cdataptr(cd);
|
||||
setcdataV(L, L->top++, cd);
|
||||
if (ctype_isstruct(ctr->info)) {
|
||||
CCALL_HANDLE_STRUCTRET
|
||||
} else {
|
||||
CCALL_HANDLE_COMPLEXRET
|
||||
}
|
||||
#if LJ_TARGET_X86
|
||||
} else if (ctype_isfp(ctr->info)) {
|
||||
cc->resx87 = ctr->size == sizeof(float) ? 1 : 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Skip initial attributes. */
|
||||
fid = ct->sib;
|
||||
while (fid) {
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
if (!ctype_isattrib(ctf->info)) break;
|
||||
fid = ctf->sib;
|
||||
}
|
||||
|
||||
/* Walk through all passed arguments. */
|
||||
for (o = L->base+1, narg = 1; o < top; o++, narg++) {
|
||||
CTypeID did;
|
||||
CType *d;
|
||||
CTSize sz;
|
||||
MSize n, isfp = 0, isva = 0;
|
||||
void *dp, *rp = NULL;
|
||||
|
||||
if (fid) { /* Get argument type from field. */
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
fid = ctf->sib;
|
||||
lua_assert(ctype_isfield(ctf->info));
|
||||
did = ctype_cid(ctf->info);
|
||||
} else {
|
||||
if (!(ct->info & CTF_VARARG))
|
||||
lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */
|
||||
did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */
|
||||
isva = 1;
|
||||
}
|
||||
d = ctype_raw(cts, did);
|
||||
sz = d->size;
|
||||
|
||||
/* Find out how (by value/ref) and where (GPR/FPR) to pass an argument. */
|
||||
if (ctype_isnum(d->info)) {
|
||||
if (sz > 8) goto err_nyi;
|
||||
if ((d->info & CTF_FP))
|
||||
isfp = 1;
|
||||
} else if (ctype_isvector(d->info)) {
|
||||
if (CCALL_VECTOR_REG && (sz == 8 || sz == 16))
|
||||
isfp = 1;
|
||||
else
|
||||
goto err_nyi;
|
||||
} else if (ctype_isstruct(d->info)) {
|
||||
CCALL_HANDLE_STRUCTARG
|
||||
} else if (ctype_iscomplex(d->info)) {
|
||||
CCALL_HANDLE_COMPLEXARG
|
||||
} else {
|
||||
sz = CTSIZE_PTR;
|
||||
}
|
||||
sz = (sz + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1);
|
||||
n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */
|
||||
|
||||
CCALL_HANDLE_REGARG /* Handle register arguments. */
|
||||
|
||||
/* Otherwise pass argument on stack. */
|
||||
if (CCALL_ALIGN_STACKARG && !rp && (d->info & CTF_ALIGN) > CTALIGN_PTR) {
|
||||
MSize align = (1u << ctype_align(d->info-CTALIGN_PTR)) -1;
|
||||
nsp = (nsp + align) & ~align; /* Align argument on stack. */
|
||||
}
|
||||
if (nsp + n > CCALL_MAXSTACK) { /* Too many arguments. */
|
||||
err_nyi:
|
||||
lj_err_caller(L, LJ_ERR_FFI_NYICALL);
|
||||
}
|
||||
dp = &cc->stack[nsp];
|
||||
nsp += n;
|
||||
isva = 0;
|
||||
|
||||
done:
|
||||
if (rp) { /* Pass by reference. */
|
||||
gcsteps++;
|
||||
*(void **)dp = rp;
|
||||
dp = rp;
|
||||
}
|
||||
lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, CCF_ARG(narg));
|
||||
/* Extend passed integers to 32 bits at least. */
|
||||
if (ctype_isinteger_or_bool(d->info) && d->size < 4) {
|
||||
if (d->info & CTF_UNSIGNED)
|
||||
*(uint32_t *)dp = d->size == 1 ? (uint32_t)*(uint8_t *)dp :
|
||||
(uint32_t)*(uint16_t *)dp;
|
||||
else
|
||||
*(int32_t *)dp = d->size == 1 ? (int32_t)*(int8_t *)dp :
|
||||
(int32_t)*(int16_t *)dp;
|
||||
}
|
||||
#if LJ_TARGET_X64 && LJ_ABI_WIN
|
||||
if (isva) { /* Windows/x64 mirrors varargs in both register sets. */
|
||||
if (nfpr == ngpr)
|
||||
cc->gpr[ngpr-1] = cc->fpr[ngpr-1].l[0];
|
||||
else
|
||||
cc->fpr[ngpr-1].l[0] = cc->gpr[ngpr-1];
|
||||
}
|
||||
#else
|
||||
UNUSED(isva);
|
||||
#endif
|
||||
#if LJ_TARGET_X64 && !LJ_ABI_WIN
|
||||
if (isfp == 2 && n == 2 && (uint8_t *)dp == (uint8_t *)&cc->fpr[nfpr-2]) {
|
||||
cc->fpr[nfpr-1].d[0] = cc->fpr[nfpr-2].d[1]; /* Split complex double. */
|
||||
cc->fpr[nfpr-2].d[1] = 0;
|
||||
}
|
||||
#else
|
||||
UNUSED(isfp);
|
||||
#endif
|
||||
}
|
||||
if (fid) lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too few arguments. */
|
||||
|
||||
#if LJ_TARGET_X64 || LJ_TARGET_PPC
|
||||
cc->nfpr = nfpr; /* Required for vararg functions. */
|
||||
#endif
|
||||
cc->nsp = nsp;
|
||||
cc->spadj = (CCALL_SPS_FREE + CCALL_SPS_EXTRA)*CTSIZE_PTR;
|
||||
if (nsp > CCALL_SPS_FREE)
|
||||
cc->spadj += (((nsp-CCALL_SPS_FREE)*CTSIZE_PTR + 15u) & ~15u);
|
||||
return gcsteps;
|
||||
}
|
||||
|
||||
/* Get results from C call. */
|
||||
static int ccall_get_results(lua_State *L, CTState *cts, CType *ct,
|
||||
CCallState *cc, int *ret)
|
||||
{
|
||||
CType *ctr = ctype_rawchild(cts, ct);
|
||||
uint8_t *sp = (uint8_t *)&cc->gpr[0];
|
||||
if (ctype_isvoid(ctr->info)) {
|
||||
*ret = 0; /* Zero results. */
|
||||
return 0; /* No additional GC step. */
|
||||
}
|
||||
*ret = 1; /* One result. */
|
||||
if (ctype_isstruct(ctr->info)) {
|
||||
/* Return cdata object which is already on top of stack. */
|
||||
if (!cc->retref) {
|
||||
void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */
|
||||
CCALL_HANDLE_STRUCTRET2
|
||||
}
|
||||
return 1; /* One GC step. */
|
||||
}
|
||||
if (ctype_iscomplex(ctr->info)) {
|
||||
/* Return cdata object which is already on top of stack. */
|
||||
void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */
|
||||
CCALL_HANDLE_COMPLEXRET2
|
||||
return 1; /* One GC step. */
|
||||
}
|
||||
if (LJ_BE && ctype_isinteger_or_bool(ctr->info) && ctr->size < CTSIZE_PTR)
|
||||
sp += (CTSIZE_PTR - ctr->size);
|
||||
#if CCALL_NUM_FPR
|
||||
if (ctype_isfp(ctr->info) || ctype_isvector(ctr->info))
|
||||
sp = (uint8_t *)&cc->fpr[0];
|
||||
#endif
|
||||
#ifdef CCALL_HANDLE_RET
|
||||
CCALL_HANDLE_RET
|
||||
#endif
|
||||
/* No reference types end up here, so there's no need for the CTypeID. */
|
||||
lua_assert(!(ctype_isrefarray(ctr->info) || ctype_isstruct(ctr->info)));
|
||||
return lj_cconv_tv_ct(cts, ctr, 0, L->top-1, sp);
|
||||
}
|
||||
|
||||
/* Call C function. */
|
||||
int lj_ccall_func(lua_State *L, GCcdata *cd)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_raw(cts, cd->ctypeid);
|
||||
CTSize sz = CTSIZE_PTR;
|
||||
if (ctype_isptr(ct->info)) {
|
||||
sz = ct->size;
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
}
|
||||
if (ctype_isfunc(ct->info)) {
|
||||
CCallState cc;
|
||||
int gcsteps, ret;
|
||||
cc.func = (void (*)(void))cdata_getptr(cdataptr(cd), sz);
|
||||
gcsteps = ccall_set_args(L, cts, ct, &cc);
|
||||
ct = (CType *)((intptr_t)ct-(intptr_t)cts->tab);
|
||||
cts->cb.slot = ~0u;
|
||||
lj_vm_ffi_call(&cc);
|
||||
if (cts->cb.slot != ~0u) { /* Blacklist function that called a callback. */
|
||||
TValue tv;
|
||||
setlightudV(&tv, (void *)cc.func);
|
||||
setboolV(lj_tab_set(L, cts->miscmap, &tv), 1);
|
||||
}
|
||||
ct = (CType *)((intptr_t)ct+(intptr_t)cts->tab); /* May be reallocated. */
|
||||
gcsteps += ccall_get_results(L, cts, ct, &cc, &ret);
|
||||
#if LJ_TARGET_X86 && LJ_ABI_WIN
|
||||
/* Automatically detect __stdcall and fix up C function declaration. */
|
||||
if (cc.spadj && ctype_cconv(ct->info) == CTCC_CDECL) {
|
||||
CTF_INSERT(ct->info, CCONV, CTCC_STDCALL);
|
||||
lj_trace_abort(G(L));
|
||||
}
|
||||
#endif
|
||||
while (gcsteps-- > 0)
|
||||
lj_gc_check(L);
|
||||
return ret;
|
||||
}
|
||||
return -1; /* Not a function. */
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
** FFI C call handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CCALL_H
|
||||
#define _LJ_CCALL_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_ctype.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
/* -- C calling conventions ----------------------------------------------- */
|
||||
|
||||
#if LJ_TARGET_X86ORX64
|
||||
|
||||
#if LJ_TARGET_X86
|
||||
#define CCALL_NARG_GPR 2 /* For fastcall arguments. */
|
||||
#define CCALL_NARG_FPR 0
|
||||
#define CCALL_NRET_GPR 2
|
||||
#define CCALL_NRET_FPR 1 /* For FP results on x87 stack. */
|
||||
#define CCALL_ALIGN_STACKARG 0 /* Don't align argument on stack. */
|
||||
#elif LJ_ABI_WIN
|
||||
#define CCALL_NARG_GPR 4
|
||||
#define CCALL_NARG_FPR 4
|
||||
#define CCALL_NRET_GPR 1
|
||||
#define CCALL_NRET_FPR 1
|
||||
#define CCALL_SPS_EXTRA 4
|
||||
#else
|
||||
#define CCALL_NARG_GPR 6
|
||||
#define CCALL_NARG_FPR 8
|
||||
#define CCALL_NRET_GPR 2
|
||||
#define CCALL_NRET_FPR 2
|
||||
#define CCALL_VECTOR_REG 1 /* Pass vectors in registers. */
|
||||
#endif
|
||||
|
||||
#define CCALL_SPS_FREE 1
|
||||
#define CCALL_ALIGN_CALLSTATE 16
|
||||
|
||||
typedef LJ_ALIGN(16) union FPRArg {
|
||||
double d[2];
|
||||
float f[4];
|
||||
uint8_t b[16];
|
||||
uint16_t s[8];
|
||||
int i[4];
|
||||
int64_t l[2];
|
||||
} FPRArg;
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
|
||||
#define CCALL_NARG_GPR 4
|
||||
#define CCALL_NRET_GPR 2 /* For softfp double. */
|
||||
#if LJ_ABI_SOFTFP
|
||||
#define CCALL_NARG_FPR 0
|
||||
#define CCALL_NRET_FPR 0
|
||||
#else
|
||||
#define CCALL_NARG_FPR 8
|
||||
#define CCALL_NRET_FPR 4
|
||||
#endif
|
||||
#define CCALL_SPS_FREE 0
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
typedef union FPRArg {
|
||||
double d;
|
||||
float f[2];
|
||||
} FPRArg;
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
|
||||
#define CCALL_NARG_GPR 8
|
||||
#define CCALL_NARG_FPR 8
|
||||
#define CCALL_NRET_GPR 4 /* For complex double. */
|
||||
#define CCALL_NRET_FPR 1
|
||||
#define CCALL_SPS_EXTRA 4
|
||||
#define CCALL_SPS_FREE 0
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
typedef double FPRArg;
|
||||
|
||||
#elif LJ_TARGET_PPCSPE
|
||||
|
||||
#define CCALL_NARG_GPR 8
|
||||
#define CCALL_NARG_FPR 0
|
||||
#define CCALL_NRET_GPR 4 /* For softfp complex double. */
|
||||
#define CCALL_NRET_FPR 0
|
||||
#define CCALL_SPS_FREE 0 /* NYI */
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
|
||||
#define CCALL_NARG_GPR 4
|
||||
#define CCALL_NARG_FPR 2
|
||||
#define CCALL_NRET_GPR 2
|
||||
#define CCALL_NRET_FPR 2
|
||||
#define CCALL_SPS_EXTRA 7
|
||||
#define CCALL_SPS_FREE 1
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
typedef union FPRArg {
|
||||
double d;
|
||||
struct { LJ_ENDIAN_LOHI(float f; , float g;) };
|
||||
} FPRArg;
|
||||
|
||||
#else
|
||||
#error "Missing calling convention definitions for this architecture"
|
||||
#endif
|
||||
|
||||
#ifndef CCALL_SPS_EXTRA
|
||||
#define CCALL_SPS_EXTRA 0
|
||||
#endif
|
||||
#ifndef CCALL_VECTOR_REG
|
||||
#define CCALL_VECTOR_REG 0
|
||||
#endif
|
||||
#ifndef CCALL_ALIGN_STACKARG
|
||||
#define CCALL_ALIGN_STACKARG 1
|
||||
#endif
|
||||
#ifndef CCALL_ALIGN_CALLSTATE
|
||||
#define CCALL_ALIGN_CALLSTATE 8
|
||||
#endif
|
||||
|
||||
#define CCALL_NUM_GPR \
|
||||
(CCALL_NARG_GPR > CCALL_NRET_GPR ? CCALL_NARG_GPR : CCALL_NRET_GPR)
|
||||
#define CCALL_NUM_FPR \
|
||||
(CCALL_NARG_FPR > CCALL_NRET_FPR ? CCALL_NARG_FPR : CCALL_NRET_FPR)
|
||||
|
||||
/* Check against constants in lj_ctype.h. */
|
||||
LJ_STATIC_ASSERT(CCALL_NUM_GPR <= CCALL_MAX_GPR);
|
||||
LJ_STATIC_ASSERT(CCALL_NUM_FPR <= CCALL_MAX_FPR);
|
||||
|
||||
#define CCALL_MAXSTACK 32
|
||||
|
||||
/* -- C call state -------------------------------------------------------- */
|
||||
|
||||
typedef LJ_ALIGN(CCALL_ALIGN_CALLSTATE) struct CCallState {
|
||||
void (*func)(void); /* Pointer to called function. */
|
||||
uint32_t spadj; /* Stack pointer adjustment. */
|
||||
uint8_t nsp; /* Number of stack slots. */
|
||||
uint8_t retref; /* Return value by reference. */
|
||||
#if LJ_TARGET_X64
|
||||
uint8_t ngpr; /* Number of arguments in GPRs. */
|
||||
uint8_t nfpr; /* Number of arguments in FPRs. */
|
||||
#elif LJ_TARGET_X86
|
||||
uint8_t resx87; /* Result on x87 stack: 1:float, 2:double. */
|
||||
#elif LJ_TARGET_PPC
|
||||
uint8_t nfpr; /* Number of arguments in FPRs. */
|
||||
#endif
|
||||
#if LJ_32
|
||||
int32_t align1;
|
||||
#endif
|
||||
#if CCALL_NUM_FPR
|
||||
FPRArg fpr[CCALL_NUM_FPR]; /* Arguments/results in FPRs. */
|
||||
#endif
|
||||
GPRArg gpr[CCALL_NUM_GPR]; /* Arguments/results in GPRs. */
|
||||
GPRArg stack[CCALL_MAXSTACK]; /* Stack slots. */
|
||||
} CCallState;
|
||||
|
||||
/* -- C call handling ----------------------------------------------------- */
|
||||
|
||||
/* Really belongs to lj_vm.h. */
|
||||
LJ_ASMF void LJ_FASTCALL lj_vm_ffi_call(CCallState *cc);
|
||||
|
||||
LJ_FUNC CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o);
|
||||
LJ_FUNC int lj_ccall_func(lua_State *L, GCcdata *cd);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,641 +0,0 @@
|
||||
/*
|
||||
** FFI C callback handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_ccall.h"
|
||||
#include "lj_ccallback.h"
|
||||
#include "lj_target.h"
|
||||
#include "lj_mcode.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_vm.h"
|
||||
|
||||
/* -- Target-specific handling of callback slots -------------------------- */
|
||||
|
||||
#define CALLBACK_MCODE_SIZE (LJ_PAGESIZE * LJ_NUM_CBPAGE)
|
||||
|
||||
#if LJ_OS_NOJIT
|
||||
|
||||
/* Disabled callback support. */
|
||||
#define CALLBACK_SLOT2OFS(slot) (0*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (0*(ofs))
|
||||
#define CALLBACK_MAX_SLOT 0
|
||||
|
||||
#elif LJ_TARGET_X86ORX64
|
||||
|
||||
#define CALLBACK_MCODE_HEAD (LJ_64 ? 8 : 0)
|
||||
#define CALLBACK_MCODE_GROUP (-2+1+2+5+(LJ_64 ? 6 : 5))
|
||||
|
||||
#define CALLBACK_SLOT2OFS(slot) \
|
||||
(CALLBACK_MCODE_HEAD + CALLBACK_MCODE_GROUP*((slot)/32) + 4*(slot))
|
||||
|
||||
static MSize CALLBACK_OFS2SLOT(MSize ofs)
|
||||
{
|
||||
MSize group;
|
||||
ofs -= CALLBACK_MCODE_HEAD;
|
||||
group = ofs / (32*4 + CALLBACK_MCODE_GROUP);
|
||||
return (ofs % (32*4 + CALLBACK_MCODE_GROUP))/4 + group*32;
|
||||
}
|
||||
|
||||
#define CALLBACK_MAX_SLOT \
|
||||
(((CALLBACK_MCODE_SIZE-CALLBACK_MCODE_HEAD)/(CALLBACK_MCODE_GROUP+4*32))*32)
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
|
||||
#define CALLBACK_MCODE_HEAD 32
|
||||
#define CALLBACK_SLOT2OFS(slot) (CALLBACK_MCODE_HEAD + 8*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (((ofs)-CALLBACK_MCODE_HEAD)/8)
|
||||
#define CALLBACK_MAX_SLOT (CALLBACK_OFS2SLOT(CALLBACK_MCODE_SIZE))
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
|
||||
#define CALLBACK_MCODE_HEAD 24
|
||||
#define CALLBACK_SLOT2OFS(slot) (CALLBACK_MCODE_HEAD + 8*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (((ofs)-CALLBACK_MCODE_HEAD)/8)
|
||||
#define CALLBACK_MAX_SLOT (CALLBACK_OFS2SLOT(CALLBACK_MCODE_SIZE))
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
|
||||
#define CALLBACK_MCODE_HEAD 24
|
||||
#define CALLBACK_SLOT2OFS(slot) (CALLBACK_MCODE_HEAD + 8*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (((ofs)-CALLBACK_MCODE_HEAD)/8)
|
||||
#define CALLBACK_MAX_SLOT (CALLBACK_OFS2SLOT(CALLBACK_MCODE_SIZE))
|
||||
|
||||
#else
|
||||
|
||||
/* Missing support for this architecture. */
|
||||
#define CALLBACK_SLOT2OFS(slot) (0*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (0*(ofs))
|
||||
#define CALLBACK_MAX_SLOT 0
|
||||
|
||||
#endif
|
||||
|
||||
/* Convert callback slot number to callback function pointer. */
|
||||
static void *callback_slot2ptr(CTState *cts, MSize slot)
|
||||
{
|
||||
return (uint8_t *)cts->cb.mcode + CALLBACK_SLOT2OFS(slot);
|
||||
}
|
||||
|
||||
/* Convert callback function pointer to slot number. */
|
||||
MSize lj_ccallback_ptr2slot(CTState *cts, void *p)
|
||||
{
|
||||
uintptr_t ofs = (uintptr_t)((uint8_t *)p -(uint8_t *)cts->cb.mcode);
|
||||
if (ofs < CALLBACK_MCODE_SIZE) {
|
||||
MSize slot = CALLBACK_OFS2SLOT((MSize)ofs);
|
||||
if (CALLBACK_SLOT2OFS(slot) == (MSize)ofs)
|
||||
return slot;
|
||||
}
|
||||
return ~0u; /* Not a known callback function pointer. */
|
||||
}
|
||||
|
||||
/* Initialize machine code for callback function pointers. */
|
||||
#if LJ_OS_NOJIT
|
||||
/* Disabled callback support. */
|
||||
#define callback_mcode_init(g, p) UNUSED(p)
|
||||
#elif LJ_TARGET_X86ORX64
|
||||
static void callback_mcode_init(global_State *g, uint8_t *page)
|
||||
{
|
||||
uint8_t *p = page;
|
||||
uint8_t *target = (uint8_t *)(void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
#if LJ_64
|
||||
*(void **)p = target; p += 8;
|
||||
#endif
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
/* mov al, slot; jmp group */
|
||||
*p++ = XI_MOVrib | RID_EAX; *p++ = (uint8_t)slot;
|
||||
if ((slot & 31) == 31 || slot == CALLBACK_MAX_SLOT-1) {
|
||||
/* push ebp/rbp; mov ah, slot>>8; mov ebp, &g. */
|
||||
*p++ = XI_PUSH + RID_EBP;
|
||||
*p++ = XI_MOVrib | (RID_EAX+4); *p++ = (uint8_t)(slot >> 8);
|
||||
*p++ = XI_MOVri | RID_EBP;
|
||||
*(int32_t *)p = i32ptr(g); p += 4;
|
||||
#if LJ_64
|
||||
/* jmp [rip-pageofs] where lj_vm_ffi_callback is stored. */
|
||||
*p++ = XI_GROUP5; *p++ = XM_OFS0 + (XOg_JMP<<3) + RID_EBP;
|
||||
*(int32_t *)p = (int32_t)(page-(p+4)); p += 4;
|
||||
#else
|
||||
/* jmp lj_vm_ffi_callback. */
|
||||
*p++ = XI_JMP; *(int32_t *)p = target-(p+4); p += 4;
|
||||
#endif
|
||||
} else {
|
||||
*p++ = XI_JMPs; *p++ = (uint8_t)((2+2)*(31-(slot&31)) - 2);
|
||||
}
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_ARM
|
||||
static void callback_mcode_init(global_State *g, uint32_t *page)
|
||||
{
|
||||
uint32_t *p = page;
|
||||
void *target = (void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
/* This must match with the saveregs macro in buildvm_arm.dasc. */
|
||||
*p++ = ARMI_SUB|ARMF_D(RID_R12)|ARMF_N(RID_R12)|ARMF_M(RID_PC);
|
||||
*p++ = ARMI_PUSH|ARMF_N(RID_SP)|RSET_RANGE(RID_R4,RID_R11+1)|RID2RSET(RID_LR);
|
||||
*p++ = ARMI_SUB|ARMI_K12|ARMF_D(RID_R12)|ARMF_N(RID_R12)|CALLBACK_MCODE_HEAD;
|
||||
*p++ = ARMI_STR|ARMI_LS_P|ARMI_LS_W|ARMF_D(RID_R12)|ARMF_N(RID_SP)|(CFRAME_SIZE-4*9);
|
||||
*p++ = ARMI_LDR|ARMI_LS_P|ARMI_LS_U|ARMF_D(RID_R12)|ARMF_N(RID_PC);
|
||||
*p++ = ARMI_LDR|ARMI_LS_P|ARMI_LS_U|ARMF_D(RID_PC)|ARMF_N(RID_PC);
|
||||
*p++ = u32ptr(g);
|
||||
*p++ = u32ptr(target);
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
*p++ = ARMI_MOV|ARMF_D(RID_R12)|ARMF_M(RID_PC);
|
||||
*p = ARMI_B | ((page-p-2) & 0x00ffffffu);
|
||||
p++;
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_PPC
|
||||
static void callback_mcode_init(global_State *g, uint32_t *page)
|
||||
{
|
||||
uint32_t *p = page;
|
||||
void *target = (void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
*p++ = PPCI_LIS | PPCF_T(RID_TMP) | (u32ptr(target) >> 16);
|
||||
*p++ = PPCI_LIS | PPCF_T(RID_R12) | (u32ptr(g) >> 16);
|
||||
*p++ = PPCI_ORI | PPCF_A(RID_TMP)|PPCF_T(RID_TMP) | (u32ptr(target) & 0xffff);
|
||||
*p++ = PPCI_ORI | PPCF_A(RID_R12)|PPCF_T(RID_R12) | (u32ptr(g) & 0xffff);
|
||||
*p++ = PPCI_MTCTR | PPCF_T(RID_TMP);
|
||||
*p++ = PPCI_BCTR;
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
*p++ = PPCI_LI | PPCF_T(RID_R11) | slot;
|
||||
*p = PPCI_B | (((page-p) & 0x00ffffffu) << 2);
|
||||
p++;
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_MIPS
|
||||
static void callback_mcode_init(global_State *g, uint32_t *page)
|
||||
{
|
||||
uint32_t *p = page;
|
||||
void *target = (void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
*p++ = MIPSI_SW | MIPSF_T(RID_R1)|MIPSF_S(RID_SP) | 0;
|
||||
*p++ = MIPSI_LUI | MIPSF_T(RID_R3) | (u32ptr(target) >> 16);
|
||||
*p++ = MIPSI_LUI | MIPSF_T(RID_R2) | (u32ptr(g) >> 16);
|
||||
*p++ = MIPSI_ORI | MIPSF_T(RID_R3)|MIPSF_S(RID_R3) |(u32ptr(target)&0xffff);
|
||||
*p++ = MIPSI_JR | MIPSF_S(RID_R3);
|
||||
*p++ = MIPSI_ORI | MIPSF_T(RID_R2)|MIPSF_S(RID_R2) | (u32ptr(g)&0xffff);
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
*p = MIPSI_B | ((page-p-1) & 0x0000ffffu);
|
||||
p++;
|
||||
*p++ = MIPSI_LI | MIPSF_T(RID_R1) | slot;
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#else
|
||||
/* Missing support for this architecture. */
|
||||
#define callback_mcode_init(g, p) UNUSED(p)
|
||||
#endif
|
||||
|
||||
/* -- Machine code management --------------------------------------------- */
|
||||
|
||||
#if LJ_TARGET_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#elif LJ_TARGET_POSIX
|
||||
|
||||
#include <sys/mman.h>
|
||||
#ifndef MAP_ANONYMOUS
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Allocate and initialize area for callback function pointers. */
|
||||
static void callback_mcode_new(CTState *cts)
|
||||
{
|
||||
size_t sz = (size_t)CALLBACK_MCODE_SIZE;
|
||||
void *p;
|
||||
if (CALLBACK_MAX_SLOT == 0)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
#if LJ_TARGET_WINDOWS
|
||||
p = VirtualAlloc(NULL, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!p)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
#elif LJ_TARGET_POSIX
|
||||
p = mmap(NULL, sz, (PROT_READ|PROT_WRITE), MAP_PRIVATE|MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
if (p == MAP_FAILED)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
#else
|
||||
/* Fallback allocator. Fails if memory is not executable by default. */
|
||||
p = lj_mem_new(cts->L, sz);
|
||||
#endif
|
||||
cts->cb.mcode = p;
|
||||
callback_mcode_init(cts->g, p);
|
||||
lj_mcode_sync(p, (char *)p + sz);
|
||||
#if LJ_TARGET_WINDOWS
|
||||
{
|
||||
DWORD oprot;
|
||||
VirtualProtect(p, sz, PAGE_EXECUTE_READ, &oprot);
|
||||
}
|
||||
#elif LJ_TARGET_POSIX
|
||||
mprotect(p, sz, (PROT_READ|PROT_EXEC));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Free area for callback function pointers. */
|
||||
void lj_ccallback_mcode_free(CTState *cts)
|
||||
{
|
||||
size_t sz = (size_t)CALLBACK_MCODE_SIZE;
|
||||
void *p = cts->cb.mcode;
|
||||
if (p == NULL) return;
|
||||
#if LJ_TARGET_WINDOWS
|
||||
VirtualFree(p, 0, MEM_RELEASE);
|
||||
UNUSED(sz);
|
||||
#elif LJ_TARGET_POSIX
|
||||
munmap(p, sz);
|
||||
#else
|
||||
lj_mem_free(cts->g, p, sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* -- C callback entry ---------------------------------------------------- */
|
||||
|
||||
/* Target-specific handling of register arguments. Similar to lj_ccall.c. */
|
||||
#if LJ_TARGET_X86
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (!isfp) { /* Only non-FP values may be passed in registers. */ \
|
||||
if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \
|
||||
if (!LJ_ABI_WIN) ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} else if (ngpr + 1 <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64 && LJ_ABI_WIN
|
||||
|
||||
/* Windows/x64 argument registers are strictly positional (use ngpr). */
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (ngpr < 4) { sp = &cts->cb.fpr[ngpr++]; nfpr = ngpr; goto done; } \
|
||||
} else { \
|
||||
if (ngpr < 4) { sp = &cts->cb.gpr[ngpr++]; goto done; } \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (nfpr + n <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr]; \
|
||||
nfpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
|
||||
#if LJ_ABI_SOFTFP
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG_FP1 UNUSED(isfp);
|
||||
#define CALLBACK_HANDLE_REGARG_FP2
|
||||
|
||||
#else
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG_FP1 \
|
||||
if (isfp) { \
|
||||
if (n == 1) { \
|
||||
if (fprodd) { \
|
||||
sp = &cts->cb.fpr[fprodd-1]; \
|
||||
fprodd = 0; \
|
||||
goto done; \
|
||||
} else if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr++]; \
|
||||
fprodd = nfpr; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr++]; \
|
||||
goto done; \
|
||||
} \
|
||||
} \
|
||||
fprodd = 0; /* No reordering after the first FP value is on stack. */ \
|
||||
} else {
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG_FP2 }
|
||||
|
||||
#endif
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
CALLBACK_HANDLE_REGARG_FP1 \
|
||||
if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} CALLBACK_HANDLE_REGARG_FP2
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr++]; \
|
||||
cta = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \
|
||||
goto done; \
|
||||
} \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
if (n > 1) { \
|
||||
lua_assert(ctype_isinteger(cta->info) && n == 2); /* int64_t. */ \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align int64_t to regpair. */ \
|
||||
} \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CALLBACK_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
*(double *)dp = *(float *)dp; /* FPRs always hold doubles. */
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp && nfpr < CCALL_NARG_FPR) { /* Try to pass argument in FPRs. */ \
|
||||
sp = (void *)((uint8_t *)&cts->cb.fpr[nfpr] + ((LJ_BE && n==1) ? 4 : 0)); \
|
||||
nfpr++; ngpr += n; \
|
||||
goto done; \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
nfpr = CCALL_NARG_FPR; \
|
||||
if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CALLBACK_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
((float *)dp)[1] = *(float *)dp;
|
||||
|
||||
#else
|
||||
#error "Missing calling convention definitions for this architecture"
|
||||
#endif
|
||||
|
||||
/* Convert and push callback arguments to Lua stack. */
|
||||
static void callback_conv_args(CTState *cts, lua_State *L)
|
||||
{
|
||||
TValue *o = L->top;
|
||||
intptr_t *stack = cts->cb.stack;
|
||||
MSize slot = cts->cb.slot;
|
||||
CTypeID id = 0, rid, fid;
|
||||
CType *ct;
|
||||
GCfunc *fn;
|
||||
MSize ngpr = 0, nsp = 0, maxgpr = CCALL_NARG_GPR;
|
||||
#if CCALL_NARG_FPR
|
||||
MSize nfpr = 0;
|
||||
#if LJ_TARGET_ARM
|
||||
MSize fprodd = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (slot < cts->cb.sizeid && (id = cts->cb.cbid[slot]) != 0) {
|
||||
ct = ctype_get(cts, id);
|
||||
rid = ctype_cid(ct->info);
|
||||
fn = funcV(lj_tab_getint(cts->miscmap, (int32_t)slot));
|
||||
} else { /* Must set up frame first, before throwing the error. */
|
||||
ct = NULL;
|
||||
rid = 0;
|
||||
fn = (GCfunc *)L;
|
||||
}
|
||||
o->u32.lo = LJ_CONT_FFI_CALLBACK; /* Continuation returns from callback. */
|
||||
o->u32.hi = rid; /* Return type. x86: +(spadj<<16). */
|
||||
o++;
|
||||
setframe_gc(o, obj2gco(fn));
|
||||
setframe_ftsz(o, (int)((char *)(o+1) - (char *)L->base) + FRAME_CONT);
|
||||
L->top = L->base = ++o;
|
||||
if (!ct)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_BADCBACK);
|
||||
if (isluafunc(fn))
|
||||
setcframe_pc(L->cframe, proto_bc(funcproto(fn))+1);
|
||||
lj_state_checkstack(L, LUA_MINSTACK); /* May throw. */
|
||||
o = L->base; /* Might have been reallocated. */
|
||||
|
||||
#if LJ_TARGET_X86
|
||||
/* x86 has several different calling conventions. */
|
||||
switch (ctype_cconv(ct->info)) {
|
||||
case CTCC_FASTCALL: maxgpr = 2; break;
|
||||
case CTCC_THISCALL: maxgpr = 1; break;
|
||||
default: maxgpr = 0; break;
|
||||
}
|
||||
#endif
|
||||
|
||||
fid = ct->sib;
|
||||
while (fid) {
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
if (!ctype_isattrib(ctf->info)) {
|
||||
CType *cta;
|
||||
void *sp;
|
||||
CTSize sz;
|
||||
int isfp;
|
||||
MSize n;
|
||||
lua_assert(ctype_isfield(ctf->info));
|
||||
cta = ctype_rawchild(cts, ctf);
|
||||
isfp = ctype_isfp(cta->info);
|
||||
sz = (cta->size + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1);
|
||||
n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */
|
||||
|
||||
CALLBACK_HANDLE_REGARG /* Handle register arguments. */
|
||||
|
||||
/* Otherwise pass argument on stack. */
|
||||
if (CCALL_ALIGN_STACKARG && LJ_32 && sz == 8)
|
||||
nsp = (nsp + 1) & ~1u; /* Align 64 bit argument on stack. */
|
||||
sp = &stack[nsp];
|
||||
nsp += n;
|
||||
|
||||
done:
|
||||
if (LJ_BE && cta->size < CTSIZE_PTR)
|
||||
sp = (void *)((uint8_t *)sp + CTSIZE_PTR-cta->size);
|
||||
lj_cconv_tv_ct(cts, cta, 0, o++, sp);
|
||||
}
|
||||
fid = ctf->sib;
|
||||
}
|
||||
L->top = o;
|
||||
#if LJ_TARGET_X86
|
||||
/* Store stack adjustment for returns from non-cdecl callbacks. */
|
||||
if (ctype_cconv(ct->info) != CTCC_CDECL)
|
||||
(L->base-2)->u32.hi |= (nsp << (16+2));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert Lua object to callback result. */
|
||||
static void callback_conv_result(CTState *cts, lua_State *L, TValue *o)
|
||||
{
|
||||
CType *ctr = ctype_raw(cts, (uint16_t)(L->base-2)->u32.hi);
|
||||
#if LJ_TARGET_X86
|
||||
cts->cb.gpr[2] = 0;
|
||||
#endif
|
||||
if (!ctype_isvoid(ctr->info)) {
|
||||
uint8_t *dp = (uint8_t *)&cts->cb.gpr[0];
|
||||
#if CCALL_NUM_FPR
|
||||
if (ctype_isfp(ctr->info))
|
||||
dp = (uint8_t *)&cts->cb.fpr[0];
|
||||
#endif
|
||||
lj_cconv_ct_tv(cts, ctr, dp, o, 0);
|
||||
#ifdef CALLBACK_HANDLE_RET
|
||||
CALLBACK_HANDLE_RET
|
||||
#endif
|
||||
/* Extend returned integers to (at least) 32 bits. */
|
||||
if (ctype_isinteger_or_bool(ctr->info) && ctr->size < 4) {
|
||||
if (ctr->info & CTF_UNSIGNED)
|
||||
*(uint32_t *)dp = ctr->size == 1 ? (uint32_t)*(uint8_t *)dp :
|
||||
(uint32_t)*(uint16_t *)dp;
|
||||
else
|
||||
*(int32_t *)dp = ctr->size == 1 ? (int32_t)*(int8_t *)dp :
|
||||
(int32_t)*(int16_t *)dp;
|
||||
}
|
||||
#if LJ_TARGET_X86
|
||||
if (ctype_isfp(ctr->info))
|
||||
cts->cb.gpr[2] = ctr->size == sizeof(float) ? 1 : 2;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Enter callback. */
|
||||
lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf)
|
||||
{
|
||||
lua_State *L = cts->L;
|
||||
global_State *g = cts->g;
|
||||
lua_assert(L != NULL);
|
||||
if (gcref(g->jit_L)) {
|
||||
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_FFI_BADCBACK));
|
||||
if (g->panic) g->panic(L);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
lj_trace_abort(g); /* Never record across callback. */
|
||||
/* Setup C frame. */
|
||||
cframe_prev(cf) = L->cframe;
|
||||
setcframe_L(cf, L);
|
||||
cframe_errfunc(cf) = -1;
|
||||
cframe_nres(cf) = 0;
|
||||
L->cframe = cf;
|
||||
callback_conv_args(cts, L);
|
||||
return L; /* Now call the function on this stack. */
|
||||
}
|
||||
|
||||
/* Leave callback. */
|
||||
void LJ_FASTCALL lj_ccallback_leave(CTState *cts, TValue *o)
|
||||
{
|
||||
lua_State *L = cts->L;
|
||||
GCfunc *fn;
|
||||
TValue *obase = L->base;
|
||||
L->base = L->top; /* Keep continuation frame for throwing errors. */
|
||||
if (o >= L->base) {
|
||||
/* PC of RET* is lost. Point to last line for result conv. errors. */
|
||||
fn = curr_func(L);
|
||||
if (isluafunc(fn)) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
setcframe_pc(L->cframe, proto_bc(pt)+pt->sizebc+1);
|
||||
}
|
||||
}
|
||||
callback_conv_result(cts, L, o);
|
||||
/* Finally drop C frame and continuation frame. */
|
||||
L->cframe = cframe_prev(L->cframe);
|
||||
L->top -= 2;
|
||||
L->base = obase;
|
||||
cts->cb.slot = 0; /* Blacklist C function that called the callback. */
|
||||
}
|
||||
|
||||
/* -- C callback management ----------------------------------------------- */
|
||||
|
||||
/* Get an unused slot in the callback slot table. */
|
||||
static MSize callback_slot_new(CTState *cts, CType *ct)
|
||||
{
|
||||
CTypeID id = ctype_typeid(cts, ct);
|
||||
CTypeID1 *cbid = cts->cb.cbid;
|
||||
MSize top;
|
||||
for (top = cts->cb.topid; top < cts->cb.sizeid; top++)
|
||||
if (LJ_LIKELY(cbid[top] == 0))
|
||||
goto found;
|
||||
#if CALLBACK_MAX_SLOT
|
||||
if (top >= CALLBACK_MAX_SLOT)
|
||||
#endif
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
if (!cts->cb.mcode)
|
||||
callback_mcode_new(cts);
|
||||
lj_mem_growvec(cts->L, cbid, cts->cb.sizeid, CALLBACK_MAX_SLOT, CTypeID1);
|
||||
cts->cb.cbid = cbid;
|
||||
memset(cbid+top, 0, (cts->cb.sizeid-top)*sizeof(CTypeID1));
|
||||
found:
|
||||
cbid[top] = id;
|
||||
cts->cb.topid = top+1;
|
||||
return top;
|
||||
}
|
||||
|
||||
/* Check for function pointer and supported argument/result types. */
|
||||
static CType *callback_checkfunc(CTState *cts, CType *ct)
|
||||
{
|
||||
int narg = 0;
|
||||
if (!ctype_isptr(ct->info) || (LJ_64 && ct->size != CTSIZE_PTR))
|
||||
return NULL;
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
if (ctype_isfunc(ct->info)) {
|
||||
CType *ctr = ctype_rawchild(cts, ct);
|
||||
CTypeID fid = ct->sib;
|
||||
if (!(ctype_isvoid(ctr->info) || ctype_isenum(ctr->info) ||
|
||||
ctype_isptr(ctr->info) || (ctype_isnum(ctr->info) && ctr->size <= 8)))
|
||||
return NULL;
|
||||
if ((ct->info & CTF_VARARG))
|
||||
return NULL;
|
||||
while (fid) {
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
if (!ctype_isattrib(ctf->info)) {
|
||||
CType *cta;
|
||||
lua_assert(ctype_isfield(ctf->info));
|
||||
cta = ctype_rawchild(cts, ctf);
|
||||
if (!(ctype_isenum(cta->info) || ctype_isptr(cta->info) ||
|
||||
(ctype_isnum(cta->info) && cta->size <= 8)) ||
|
||||
++narg >= LUA_MINSTACK-3)
|
||||
return NULL;
|
||||
}
|
||||
fid = ctf->sib;
|
||||
}
|
||||
return ct;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create a new callback and return the callback function pointer. */
|
||||
void *lj_ccallback_new(CTState *cts, CType *ct, GCfunc *fn)
|
||||
{
|
||||
ct = callback_checkfunc(cts, ct);
|
||||
if (ct) {
|
||||
MSize slot = callback_slot_new(cts, ct);
|
||||
GCtab *t = cts->miscmap;
|
||||
setfuncV(cts->L, lj_tab_setint(cts->L, t, (int32_t)slot), fn);
|
||||
lj_gc_anybarriert(cts->L, t);
|
||||
return callback_slot2ptr(cts, slot);
|
||||
}
|
||||
return NULL; /* Bad conversion. */
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
** Trace recorder for C data operations.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CRECORD_H
|
||||
#define _LJ_CRECORD_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_jit.h"
|
||||
#include "lj_ffrecord.h"
|
||||
|
||||
#if LJ_HASJIT && LJ_HASFFI
|
||||
LJ_FUNC void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_cdata_call(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_clib_index(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_new(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_errno(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_string(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_copy(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_fill(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_typeof(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_istype(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_abi(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_xof(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL recff_ffi_gc(jit_State *J, RecordFFData *rd);
|
||||
LJ_FUNC void LJ_FASTCALL lj_crecord_tonumber(jit_State *J, RecordFFData *rd);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,596 +0,0 @@
|
||||
/*
|
||||
** Debugging and introspection.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_debug_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_jit.h"
|
||||
#endif
|
||||
|
||||
/* -- Frames -------------------------------------------------------------- */
|
||||
|
||||
/* Get frame corresponding to a level. */
|
||||
cTValue *lj_debug_frame(lua_State *L, int level, int *size)
|
||||
{
|
||||
cTValue *frame, *nextframe, *bot = tvref(L->stack);
|
||||
/* Traverse frames backwards. */
|
||||
for (nextframe = frame = L->base-1; frame > bot; ) {
|
||||
if (frame_gc(frame) == obj2gco(L))
|
||||
level++; /* Skip dummy frames. See lj_meta_call(). */
|
||||
if (level-- == 0) {
|
||||
*size = (int)(nextframe - frame);
|
||||
return frame; /* Level found. */
|
||||
}
|
||||
nextframe = frame;
|
||||
if (frame_islua(frame)) {
|
||||
frame = frame_prevl(frame);
|
||||
} else {
|
||||
if (frame_isvarg(frame))
|
||||
level++; /* Skip vararg pseudo-frame. */
|
||||
frame = frame_prevd(frame);
|
||||
}
|
||||
}
|
||||
*size = level;
|
||||
return NULL; /* Level not found. */
|
||||
}
|
||||
|
||||
/* Invalid bytecode position. */
|
||||
#define NO_BCPOS (~(BCPos)0)
|
||||
|
||||
/* Return bytecode position for function/frame or NO_BCPOS. */
|
||||
static BCPos debug_framepc(lua_State *L, GCfunc *fn, cTValue *nextframe)
|
||||
{
|
||||
const BCIns *ins;
|
||||
GCproto *pt;
|
||||
BCPos pos;
|
||||
lua_assert(fn->c.gct == ~LJ_TFUNC || fn->c.gct == ~LJ_TTHREAD);
|
||||
if (!isluafunc(fn)) { /* Cannot derive a PC for non-Lua functions. */
|
||||
return NO_BCPOS;
|
||||
} else if (nextframe == NULL) { /* Lua function on top. */
|
||||
void *cf = cframe_raw(L->cframe);
|
||||
if (cf == NULL || (char *)cframe_pc(cf) == (char *)cframe_L(cf))
|
||||
return NO_BCPOS;
|
||||
ins = cframe_pc(cf); /* Only happens during error/hook handling. */
|
||||
} else {
|
||||
if (frame_islua(nextframe)) {
|
||||
ins = frame_pc(nextframe);
|
||||
} else if (frame_iscont(nextframe)) {
|
||||
ins = frame_contpc(nextframe);
|
||||
} else {
|
||||
/* Lua function below errfunc/gc/hook: find cframe to get the PC. */
|
||||
void *cf = cframe_raw(L->cframe);
|
||||
TValue *f = L->base-1;
|
||||
if (cf == NULL)
|
||||
return NO_BCPOS;
|
||||
while (f > nextframe) {
|
||||
if (frame_islua(f)) {
|
||||
f = frame_prevl(f);
|
||||
} else {
|
||||
if (frame_isc(f))
|
||||
cf = cframe_raw(cframe_prev(cf));
|
||||
f = frame_prevd(f);
|
||||
}
|
||||
}
|
||||
if (cframe_prev(cf))
|
||||
cf = cframe_raw(cframe_prev(cf));
|
||||
ins = cframe_pc(cf);
|
||||
}
|
||||
}
|
||||
pt = funcproto(fn);
|
||||
pos = proto_bcpos(pt, ins) - 1;
|
||||
#if LJ_HASJIT
|
||||
if (pos > pt->sizebc) { /* Undo the effects of lj_trace_exit for JLOOP. */
|
||||
GCtrace *T = (GCtrace *)((char *)(ins-1) - offsetof(GCtrace, startins));
|
||||
lua_assert(bc_isret(bc_op(ins[-1])));
|
||||
pos = proto_bcpos(pt, mref(T->startpc, const BCIns));
|
||||
}
|
||||
#endif
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* -- Line numbers -------------------------------------------------------- */
|
||||
|
||||
/* Get line number for a bytecode position. */
|
||||
BCLine LJ_FASTCALL lj_debug_line(GCproto *pt, BCPos pc)
|
||||
{
|
||||
const void *lineinfo = proto_lineinfo(pt);
|
||||
if (pc <= pt->sizebc && lineinfo) {
|
||||
BCLine first = pt->firstline;
|
||||
if (pc == pt->sizebc) return first + pt->numline;
|
||||
if (pc-- == 0) return first;
|
||||
if (pt->numline < 256)
|
||||
return first + (BCLine)((const uint8_t *)lineinfo)[pc];
|
||||
else if (pt->numline < 65536)
|
||||
return first + (BCLine)((const uint16_t *)lineinfo)[pc];
|
||||
else
|
||||
return first + (BCLine)((const uint32_t *)lineinfo)[pc];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get line number for function/frame. */
|
||||
static BCLine debug_frameline(lua_State *L, GCfunc *fn, cTValue *nextframe)
|
||||
{
|
||||
BCPos pc = debug_framepc(L, fn, nextframe);
|
||||
if (pc != NO_BCPOS) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
lua_assert(pc <= pt->sizebc);
|
||||
return lj_debug_line(pt, pc);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* -- Variable names ------------------------------------------------------ */
|
||||
|
||||
/* Read ULEB128 value. */
|
||||
static uint32_t debug_read_uleb128(const uint8_t **pp)
|
||||
{
|
||||
const uint8_t *p = *pp;
|
||||
uint32_t v = *p++;
|
||||
if (LJ_UNLIKELY(v >= 0x80)) {
|
||||
int sh = 0;
|
||||
v &= 0x7f;
|
||||
do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80);
|
||||
}
|
||||
*pp = p;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Get name of a local variable from slot number and PC. */
|
||||
static const char *debug_varname(const GCproto *pt, BCPos pc, BCReg slot)
|
||||
{
|
||||
const uint8_t *p = proto_varinfo(pt);
|
||||
if (p) {
|
||||
BCPos lastpc = 0;
|
||||
for (;;) {
|
||||
const char *name = (const char *)p;
|
||||
uint32_t vn = *p++;
|
||||
BCPos startpc, endpc;
|
||||
if (vn < VARNAME__MAX) {
|
||||
if (vn == VARNAME_END) break; /* End of varinfo. */
|
||||
} else {
|
||||
while (*p++) ; /* Skip over variable name string. */
|
||||
}
|
||||
lastpc = startpc = lastpc + debug_read_uleb128(&p);
|
||||
if (startpc > pc) break;
|
||||
endpc = startpc + debug_read_uleb128(&p);
|
||||
if (pc < endpc && slot-- == 0) {
|
||||
if (vn < VARNAME__MAX) {
|
||||
#define VARNAMESTR(name, str) str "\0"
|
||||
name = VARNAMEDEF(VARNAMESTR);
|
||||
#undef VARNAMESTR
|
||||
if (--vn) while (*name++ || --vn) ;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get name of local variable from 1-based slot number and function/frame. */
|
||||
static TValue *debug_localname(lua_State *L, const lua_Debug *ar,
|
||||
const char **name, BCReg slot1)
|
||||
{
|
||||
uint32_t offset = (uint32_t)ar->i_ci & 0xffff;
|
||||
uint32_t size = (uint32_t)ar->i_ci >> 16;
|
||||
TValue *frame = tvref(L->stack) + offset;
|
||||
TValue *nextframe = size ? frame + size : NULL;
|
||||
GCfunc *fn = frame_func(frame);
|
||||
BCPos pc = debug_framepc(L, fn, nextframe);
|
||||
if (!nextframe) nextframe = L->top;
|
||||
if ((int)slot1 < 0) { /* Negative slot number is for varargs. */
|
||||
if (pc != NO_BCPOS) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
if ((pt->flags & PROTO_VARARG)) {
|
||||
slot1 = pt->numparams + (BCReg)(-(int)slot1);
|
||||
if (frame_isvarg(frame)) { /* Vararg frame has been set up? (pc!=0) */
|
||||
nextframe = frame;
|
||||
frame = frame_prevd(frame);
|
||||
}
|
||||
if (frame + slot1 < nextframe) {
|
||||
*name = "(*vararg)";
|
||||
return frame+slot1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (pc != NO_BCPOS &&
|
||||
(*name = debug_varname(funcproto(fn), pc, slot1-1)) != NULL)
|
||||
;
|
||||
else if (slot1 > 0 && frame + slot1 < nextframe)
|
||||
*name = "(*temporary)";
|
||||
return frame+slot1;
|
||||
}
|
||||
|
||||
/* Get name of upvalue. */
|
||||
const char *lj_debug_uvname(GCproto *pt, uint32_t idx)
|
||||
{
|
||||
const uint8_t *p = proto_uvinfo(pt);
|
||||
lua_assert(idx < pt->sizeuv);
|
||||
if (!p) return "";
|
||||
if (idx) while (*p++ || --idx) ;
|
||||
return (const char *)p;
|
||||
}
|
||||
|
||||
/* Get name and value of upvalue. */
|
||||
const char *lj_debug_uvnamev(cTValue *o, uint32_t idx, TValue **tvp)
|
||||
{
|
||||
if (tvisfunc(o)) {
|
||||
GCfunc *fn = funcV(o);
|
||||
if (isluafunc(fn)) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
if (idx < pt->sizeuv) {
|
||||
*tvp = uvval(&gcref(fn->l.uvptr[idx])->uv);
|
||||
return lj_debug_uvname(pt, idx);
|
||||
}
|
||||
} else {
|
||||
if (idx < fn->c.nupvalues) {
|
||||
*tvp = &fn->c.upvalue[idx];
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Deduce name of an object from slot number and PC. */
|
||||
const char *lj_debug_slotname(GCproto *pt, const BCIns *ip, BCReg slot,
|
||||
const char **name)
|
||||
{
|
||||
const char *lname;
|
||||
restart:
|
||||
lname = debug_varname(pt, proto_bcpos(pt, ip), slot);
|
||||
if (lname != NULL) { *name = lname; return "local"; }
|
||||
while (--ip > proto_bc(pt)) {
|
||||
BCIns ins = *ip;
|
||||
BCOp op = bc_op(ins);
|
||||
BCReg ra = bc_a(ins);
|
||||
if (bcmode_a(op) == BCMbase) {
|
||||
if (slot >= ra && (op != BC_KNIL || slot <= bc_d(ins)))
|
||||
return NULL;
|
||||
} else if (bcmode_a(op) == BCMdst && ra == slot) {
|
||||
switch (bc_op(ins)) {
|
||||
case BC_MOV:
|
||||
if (ra == slot) { slot = bc_d(ins); goto restart; }
|
||||
break;
|
||||
case BC_GGET:
|
||||
*name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_d(ins))));
|
||||
return "global";
|
||||
case BC_TGETS:
|
||||
*name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_c(ins))));
|
||||
if (ip > proto_bc(pt)) {
|
||||
BCIns insp = ip[-1];
|
||||
if (bc_op(insp) == BC_MOV && bc_a(insp) == ra+1 &&
|
||||
bc_d(insp) == bc_b(ins))
|
||||
return "method";
|
||||
}
|
||||
return "field";
|
||||
case BC_UGET:
|
||||
*name = lj_debug_uvname(pt, bc_d(ins));
|
||||
return "upvalue";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Deduce function name from caller of a frame. */
|
||||
const char *lj_debug_funcname(lua_State *L, TValue *frame, const char **name)
|
||||
{
|
||||
TValue *pframe;
|
||||
GCfunc *fn;
|
||||
BCPos pc;
|
||||
if (frame <= tvref(L->stack))
|
||||
return NULL;
|
||||
if (frame_isvarg(frame))
|
||||
frame = frame_prevd(frame);
|
||||
pframe = frame_prev(frame);
|
||||
fn = frame_func(pframe);
|
||||
pc = debug_framepc(L, fn, frame);
|
||||
if (pc != NO_BCPOS) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
const BCIns *ip = &proto_bc(pt)[check_exp(pc < pt->sizebc, pc)];
|
||||
MMS mm = bcmode_mm(bc_op(*ip));
|
||||
if (mm == MM_call) {
|
||||
BCReg slot = bc_a(*ip);
|
||||
if (bc_op(*ip) == BC_ITERC) slot -= 3;
|
||||
return lj_debug_slotname(pt, ip, slot, name);
|
||||
} else if (mm != MM__MAX) {
|
||||
*name = strdata(mmname_str(G(L), mm));
|
||||
return "metamethod";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* -- Source code locations ----------------------------------------------- */
|
||||
|
||||
/* Generate shortened source name. */
|
||||
void lj_debug_shortname(char *out, GCstr *str)
|
||||
{
|
||||
const char *src = strdata(str);
|
||||
if (*src == '=') {
|
||||
strncpy(out, src+1, LUA_IDSIZE); /* Remove first char. */
|
||||
out[LUA_IDSIZE-1] = '\0'; /* Ensures null termination. */
|
||||
} else if (*src == '@') { /* Output "source", or "...source". */
|
||||
size_t len = str->len-1;
|
||||
src++; /* Skip the `@' */
|
||||
if (len >= LUA_IDSIZE) {
|
||||
src += len-(LUA_IDSIZE-4); /* Get last part of file name. */
|
||||
*out++ = '.'; *out++ = '.'; *out++ = '.';
|
||||
}
|
||||
strcpy(out, src);
|
||||
} else { /* Output [string "string"]. */
|
||||
size_t len; /* Length, up to first control char. */
|
||||
for (len = 0; len < LUA_IDSIZE-12; len++)
|
||||
if (((const unsigned char *)src)[len] < ' ') break;
|
||||
strcpy(out, "[string \""); out += 9;
|
||||
if (src[len] != '\0') { /* Must truncate? */
|
||||
if (len > LUA_IDSIZE-15) len = LUA_IDSIZE-15;
|
||||
strncpy(out, src, len); out += len;
|
||||
strcpy(out, "..."); out += 3;
|
||||
} else {
|
||||
strcpy(out, src); out += len;
|
||||
}
|
||||
strcpy(out, "\"]");
|
||||
}
|
||||
}
|
||||
|
||||
/* Add current location of a frame to error message. */
|
||||
void lj_debug_addloc(lua_State *L, const char *msg,
|
||||
cTValue *frame, cTValue *nextframe)
|
||||
{
|
||||
if (frame) {
|
||||
GCfunc *fn = frame_func(frame);
|
||||
if (isluafunc(fn)) {
|
||||
BCLine line = debug_frameline(L, fn, nextframe);
|
||||
if (line >= 0) {
|
||||
char buf[LUA_IDSIZE];
|
||||
lj_debug_shortname(buf, proto_chunkname(funcproto(fn)));
|
||||
lj_str_pushf(L, "%s:%d: %s", buf, line, msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
lj_str_pushf(L, "%s", msg);
|
||||
}
|
||||
|
||||
/* Push location string for a bytecode position to Lua stack. */
|
||||
void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc)
|
||||
{
|
||||
GCstr *name = proto_chunkname(pt);
|
||||
const char *s = strdata(name);
|
||||
MSize i, len = name->len;
|
||||
BCLine line = lj_debug_line(pt, pc);
|
||||
if (*s == '@') {
|
||||
s++; len--;
|
||||
for (i = len; i > 0; i--)
|
||||
if (s[i] == '/' || s[i] == '\\') {
|
||||
s += i+1;
|
||||
break;
|
||||
}
|
||||
lj_str_pushf(L, "%s:%d", s, line);
|
||||
} else if (len > 40) {
|
||||
lj_str_pushf(L, "%p:%d", pt, line);
|
||||
} else if (*s == '=') {
|
||||
lj_str_pushf(L, "%s:%d", s+1, line);
|
||||
} else {
|
||||
lj_str_pushf(L, "\"%s\":%d", s, line);
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Public debug API ---------------------------------------------------- */
|
||||
|
||||
/* lua_getupvalue() and lua_setupvalue() are in lj_api.c. */
|
||||
|
||||
LUA_API const char *lua_getlocal(lua_State *L, const lua_Debug *ar, int n)
|
||||
{
|
||||
const char *name = NULL;
|
||||
if (ar) {
|
||||
TValue *o = debug_localname(L, ar, &name, (BCReg)n);
|
||||
if (name) {
|
||||
copyTV(L, L->top, o);
|
||||
incr_top(L);
|
||||
}
|
||||
} else if (tvisfunc(L->top-1) && isluafunc(funcV(L->top-1))) {
|
||||
name = debug_varname(funcproto(funcV(L->top-1)), 0, (BCReg)n-1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
LUA_API const char *lua_setlocal(lua_State *L, const lua_Debug *ar, int n)
|
||||
{
|
||||
const char *name = NULL;
|
||||
TValue *o = debug_localname(L, ar, &name, (BCReg)n);
|
||||
if (name)
|
||||
copyTV(L, o, L->top-1);
|
||||
L->top--;
|
||||
return name;
|
||||
}
|
||||
|
||||
int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, int ext)
|
||||
{
|
||||
int opt_f = 0, opt_L = 0;
|
||||
TValue *frame = NULL;
|
||||
TValue *nextframe = NULL;
|
||||
GCfunc *fn;
|
||||
if (*what == '>') {
|
||||
TValue *func = L->top - 1;
|
||||
api_check(L, tvisfunc(func));
|
||||
fn = funcV(func);
|
||||
L->top--;
|
||||
what++;
|
||||
} else {
|
||||
uint32_t offset = (uint32_t)ar->i_ci & 0xffff;
|
||||
uint32_t size = (uint32_t)ar->i_ci >> 16;
|
||||
lua_assert(offset != 0);
|
||||
frame = tvref(L->stack) + offset;
|
||||
if (size) nextframe = frame + size;
|
||||
lua_assert(frame <= tvref(L->maxstack) &&
|
||||
(!nextframe || nextframe <= tvref(L->maxstack)));
|
||||
fn = frame_func(frame);
|
||||
lua_assert(fn->c.gct == ~LJ_TFUNC);
|
||||
}
|
||||
for (; *what; what++) {
|
||||
if (*what == 'S') {
|
||||
if (isluafunc(fn)) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
BCLine firstline = pt->firstline;
|
||||
GCstr *name = proto_chunkname(pt);
|
||||
ar->source = strdata(name);
|
||||
lj_debug_shortname(ar->short_src, name);
|
||||
ar->linedefined = (int)firstline;
|
||||
ar->lastlinedefined = (int)(firstline + pt->numline);
|
||||
ar->what = firstline ? "Lua" : "main";
|
||||
} else {
|
||||
ar->source = "=[C]";
|
||||
ar->short_src[0] = '[';
|
||||
ar->short_src[1] = 'C';
|
||||
ar->short_src[2] = ']';
|
||||
ar->short_src[3] = '\0';
|
||||
ar->linedefined = -1;
|
||||
ar->lastlinedefined = -1;
|
||||
ar->what = "C";
|
||||
}
|
||||
} else if (*what == 'l') {
|
||||
ar->currentline = frame ? debug_frameline(L, fn, nextframe) : -1;
|
||||
} else if (*what == 'u') {
|
||||
ar->nups = fn->c.nupvalues;
|
||||
if (ext) {
|
||||
if (isluafunc(fn)) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
ar->nparams = pt->numparams;
|
||||
ar->isvararg = !!(pt->flags & PROTO_VARARG);
|
||||
} else {
|
||||
ar->nparams = 0;
|
||||
ar->isvararg = 1;
|
||||
}
|
||||
}
|
||||
} else if (*what == 'n') {
|
||||
ar->namewhat = frame ? lj_debug_funcname(L, frame, &ar->name) : NULL;
|
||||
if (ar->namewhat == NULL) {
|
||||
ar->namewhat = "";
|
||||
ar->name = NULL;
|
||||
}
|
||||
} else if (*what == 'f') {
|
||||
opt_f = 1;
|
||||
} else if (*what == 'L') {
|
||||
opt_L = 1;
|
||||
} else {
|
||||
return 0; /* Bad option. */
|
||||
}
|
||||
}
|
||||
if (opt_f) {
|
||||
setfuncV(L, L->top, fn);
|
||||
incr_top(L);
|
||||
}
|
||||
if (opt_L) {
|
||||
if (isluafunc(fn)) {
|
||||
GCtab *t = lj_tab_new(L, 0, 0);
|
||||
GCproto *pt = funcproto(fn);
|
||||
const void *lineinfo = proto_lineinfo(pt);
|
||||
if (lineinfo) {
|
||||
BCLine first = pt->firstline;
|
||||
int sz = pt->numline < 256 ? 1 : pt->numline < 65536 ? 2 : 4;
|
||||
MSize i, szl = pt->sizebc-1;
|
||||
for (i = 0; i < szl; i++) {
|
||||
BCLine line = first +
|
||||
(sz == 1 ? (BCLine)((const uint8_t *)lineinfo)[i] :
|
||||
sz == 2 ? (BCLine)((const uint16_t *)lineinfo)[i] :
|
||||
(BCLine)((const uint32_t *)lineinfo)[i]);
|
||||
setboolV(lj_tab_setint(L, t, line), 1);
|
||||
}
|
||||
}
|
||||
settabV(L, L->top, t);
|
||||
} else {
|
||||
setnilV(L->top);
|
||||
}
|
||||
incr_top(L);
|
||||
}
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
|
||||
LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar)
|
||||
{
|
||||
return lj_debug_getinfo(L, what, (lj_Debug *)ar, 0);
|
||||
}
|
||||
|
||||
LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar)
|
||||
{
|
||||
int size;
|
||||
cTValue *frame = lj_debug_frame(L, level, &size);
|
||||
if (frame) {
|
||||
ar->i_ci = (size << 16) + (int)(frame - tvref(L->stack));
|
||||
return 1;
|
||||
} else {
|
||||
ar->i_ci = level - size;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Number of frames for the leading and trailing part of a traceback. */
|
||||
#define TRACEBACK_LEVELS1 12
|
||||
#define TRACEBACK_LEVELS2 10
|
||||
|
||||
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
|
||||
int level)
|
||||
{
|
||||
int top = (int)(L->top - L->base);
|
||||
int lim = TRACEBACK_LEVELS1;
|
||||
lua_Debug ar;
|
||||
if (msg) lua_pushfstring(L, "%s\n", msg);
|
||||
lua_pushliteral(L, "stack traceback:");
|
||||
while (lua_getstack(L1, level++, &ar)) {
|
||||
GCfunc *fn;
|
||||
if (level > lim) {
|
||||
if (!lua_getstack(L1, level + TRACEBACK_LEVELS2, &ar)) {
|
||||
level--;
|
||||
} else {
|
||||
lua_pushliteral(L, "\n\t...");
|
||||
lua_getstack(L1, -10, &ar);
|
||||
level = ar.i_ci - TRACEBACK_LEVELS2;
|
||||
}
|
||||
lim = 2147483647;
|
||||
continue;
|
||||
}
|
||||
lua_getinfo(L1, "Snlf", &ar);
|
||||
fn = funcV(L1->top-1); L1->top--;
|
||||
if (isffunc(fn) && !*ar.namewhat)
|
||||
lua_pushfstring(L, "\n\t[builtin#%d]:", fn->c.ffid);
|
||||
else
|
||||
lua_pushfstring(L, "\n\t%s:", ar.short_src);
|
||||
if (ar.currentline > 0)
|
||||
lua_pushfstring(L, "%d:", ar.currentline);
|
||||
if (*ar.namewhat) {
|
||||
lua_pushfstring(L, " in function " LUA_QS, ar.name);
|
||||
} else {
|
||||
if (*ar.what == 'm') {
|
||||
lua_pushliteral(L, " in main chunk");
|
||||
} else if (*ar.what == 'C') {
|
||||
lua_pushfstring(L, " at %p", fn->c.f);
|
||||
} else {
|
||||
lua_pushfstring(L, " in function <%s:%d>",
|
||||
ar.short_src, ar.linedefined);
|
||||
}
|
||||
}
|
||||
if ((int)(L->top - L->base) - top >= 15)
|
||||
lua_concat(L, (int)(L->top - L->base) - top);
|
||||
}
|
||||
lua_concat(L, (int)(L->top - L->base) - top);
|
||||
}
|
||||
|
||||
@@ -1,349 +0,0 @@
|
||||
/*
|
||||
** LuaJIT common internal definitions.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_DEF_H
|
||||
#define _LJ_DEF_H
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#ifdef _WIN64
|
||||
typedef __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else
|
||||
typedef __int32 intptr_t;
|
||||
typedef unsigned __int32 uintptr_t;
|
||||
#endif
|
||||
#elif defined(__symbian__)
|
||||
/* Cough. */
|
||||
typedef signed char int8_t;
|
||||
typedef short int int16_t;
|
||||
typedef int int32_t;
|
||||
typedef long long int64_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short int uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef int intptr_t;
|
||||
typedef unsigned int uintptr_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Needed everywhere. */
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Various VM limits. */
|
||||
#define LJ_MAX_MEM 0x7fffff00 /* Max. total memory allocation. */
|
||||
#define LJ_MAX_ALLOC LJ_MAX_MEM /* Max. individual allocation length. */
|
||||
#define LJ_MAX_STR LJ_MAX_MEM /* Max. string length. */
|
||||
#define LJ_MAX_UDATA LJ_MAX_MEM /* Max. userdata length. */
|
||||
|
||||
#define LJ_MAX_STRTAB (1<<26) /* Max. string table size. */
|
||||
#define LJ_MAX_HBITS 26 /* Max. hash bits. */
|
||||
#define LJ_MAX_ABITS 28 /* Max. bits of array key. */
|
||||
#define LJ_MAX_ASIZE ((1<<(LJ_MAX_ABITS-1))+1) /* Max. array part size. */
|
||||
#define LJ_MAX_COLOSIZE 16 /* Max. elems for colocated array. */
|
||||
|
||||
#define LJ_MAX_LINE LJ_MAX_MEM /* Max. source code line number. */
|
||||
#define LJ_MAX_XLEVEL 200 /* Max. syntactic nesting level. */
|
||||
#define LJ_MAX_BCINS (1<<26) /* Max. # of bytecode instructions. */
|
||||
#define LJ_MAX_SLOTS 250 /* Max. # of slots in a Lua func. */
|
||||
#define LJ_MAX_LOCVAR 200 /* Max. # of local variables. */
|
||||
#define LJ_MAX_UPVAL 60 /* Max. # of upvalues. */
|
||||
|
||||
#define LJ_MAX_IDXCHAIN 100 /* __index/__newindex chain limit. */
|
||||
#define LJ_STACK_EXTRA 5 /* Extra stack space (metamethods). */
|
||||
|
||||
#define LJ_NUM_CBPAGE 1 /* Number of FFI callback pages. */
|
||||
|
||||
/* Minimum table/buffer sizes. */
|
||||
#define LJ_MIN_GLOBAL 6 /* Min. global table size (hbits). */
|
||||
#define LJ_MIN_REGISTRY 2 /* Min. registry size (hbits). */
|
||||
#define LJ_MIN_STRTAB 256 /* Min. string table size (pow2). */
|
||||
#define LJ_MIN_SBUF 32 /* Min. string buffer length. */
|
||||
#define LJ_MIN_VECSZ 8 /* Min. size for growable vectors. */
|
||||
#define LJ_MIN_IRSZ 32 /* Min. size for growable IR. */
|
||||
#define LJ_MIN_K64SZ 16 /* Min. size for chained K64Array. */
|
||||
|
||||
/* JIT compiler limits. */
|
||||
#define LJ_MAX_JSLOTS 250 /* Max. # of stack slots for a trace. */
|
||||
#define LJ_MAX_PHI 64 /* Max. # of PHIs for a loop. */
|
||||
#define LJ_MAX_EXITSTUBGR 16 /* Max. # of exit stub groups. */
|
||||
|
||||
/* Various macros. */
|
||||
#ifndef UNUSED
|
||||
#define UNUSED(x) ((void)(x)) /* to avoid warnings */
|
||||
#endif
|
||||
|
||||
#define U64x(hi, lo) (((uint64_t)0x##hi << 32) + (uint64_t)0x##lo)
|
||||
#define i32ptr(p) ((int32_t)(intptr_t)(void *)(p))
|
||||
#define u32ptr(p) ((uint32_t)(intptr_t)(void *)(p))
|
||||
|
||||
#define checki8(x) ((x) == (int32_t)(int8_t)(x))
|
||||
#define checku8(x) ((x) == (int32_t)(uint8_t)(x))
|
||||
#define checki16(x) ((x) == (int32_t)(int16_t)(x))
|
||||
#define checku16(x) ((x) == (int32_t)(uint16_t)(x))
|
||||
#define checki32(x) ((x) == (int32_t)(x))
|
||||
#define checku32(x) ((x) == (uint32_t)(x))
|
||||
#define checkptr32(x) ((uintptr_t)(x) == (uint32_t)(uintptr_t)(x))
|
||||
|
||||
/* Every half-decent C compiler transforms this into a rotate instruction. */
|
||||
#define lj_rol(x, n) (((x)<<(n)) | ((x)>>(8*sizeof(x)-(n))))
|
||||
#define lj_ror(x, n) (((x)<<(8*sizeof(x)-(n))) | ((x)>>(n)))
|
||||
|
||||
/* A really naive Bloom filter. But sufficient for our needs. */
|
||||
typedef uintptr_t BloomFilter;
|
||||
#define BLOOM_MASK (8*sizeof(BloomFilter) - 1)
|
||||
#define bloombit(x) ((uintptr_t)1 << ((x) & BLOOM_MASK))
|
||||
#define bloomset(b, x) ((b) |= bloombit((x)))
|
||||
#define bloomtest(b, x) ((b) & bloombit((x)))
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
#define LJ_NORET __attribute__((noreturn))
|
||||
#define LJ_ALIGN(n) __attribute__((aligned(n)))
|
||||
#define LJ_INLINE inline
|
||||
#define LJ_AINLINE inline __attribute__((always_inline))
|
||||
#define LJ_NOINLINE __attribute__((noinline))
|
||||
|
||||
#if defined(__ELF__) || defined(__MACH__)
|
||||
#if !((defined(__sun__) && defined(__svr4__)) || defined(__CELLOS_LV2__))
|
||||
#define LJ_NOAPI extern __attribute__((visibility("hidden")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Note: it's only beneficial to use fastcall on x86 and then only for up to
|
||||
** two non-FP args. The amalgamated compile covers all LJ_FUNC cases. Only
|
||||
** indirect calls and related tail-called C functions are marked as fastcall.
|
||||
*/
|
||||
#if defined(__i386__)
|
||||
#define LJ_FASTCALL __attribute__((fastcall))
|
||||
#endif
|
||||
|
||||
#define LJ_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
#define LJ_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
#define lj_ffs(x) ((uint32_t)__builtin_ctz(x))
|
||||
/* Don't ask ... */
|
||||
#if defined(__INTEL_COMPILER) && (defined(__i386__) || defined(__x86_64__))
|
||||
static LJ_AINLINE uint32_t lj_fls(uint32_t x)
|
||||
{
|
||||
uint32_t r; __asm__("bsrl %1, %0" : "=r" (r) : "rm" (x) : "cc"); return r;
|
||||
}
|
||||
#else
|
||||
#define lj_fls(x) ((uint32_t)(__builtin_clz(x)^31))
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
static LJ_AINLINE uint32_t lj_bswap(uint32_t x)
|
||||
{
|
||||
uint32_t r;
|
||||
#if __ARM_ARCH_6__ || __ARM_ARCH_6J__ || __ARM_ARCH_6T2__ || __ARM_ARCH_6Z__ ||\
|
||||
__ARM_ARCH_6ZK__ || __ARM_ARCH_7__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__
|
||||
__asm__("rev %0, %1" : "=r" (r) : "r" (x));
|
||||
return r;
|
||||
#else
|
||||
#ifdef __thumb__
|
||||
r = x ^ lj_ror(x, 16);
|
||||
#else
|
||||
__asm__("eor %0, %1, %1, ror #16" : "=r" (r) : "r" (x));
|
||||
#endif
|
||||
return ((r & 0xff00ffffu) >> 8) ^ lj_ror(x, 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
static LJ_AINLINE uint64_t lj_bswap64(uint64_t x)
|
||||
{
|
||||
return ((uint64_t)lj_bswap((uint32_t)x)<<32) | lj_bswap((uint32_t)(x>>32));
|
||||
}
|
||||
#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
|
||||
static LJ_AINLINE uint32_t lj_bswap(uint32_t x)
|
||||
{
|
||||
return (uint32_t)__builtin_bswap32((int32_t)x);
|
||||
}
|
||||
|
||||
static LJ_AINLINE uint64_t lj_bswap64(uint64_t x)
|
||||
{
|
||||
return (uint64_t)__builtin_bswap64((int64_t)x);
|
||||
}
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
static LJ_AINLINE uint32_t lj_bswap(uint32_t x)
|
||||
{
|
||||
uint32_t r; __asm__("bswap %0" : "=r" (r) : "0" (x)); return r;
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
static LJ_AINLINE uint64_t lj_bswap64(uint64_t x)
|
||||
{
|
||||
return ((uint64_t)lj_bswap((uint32_t)x)<<32) | lj_bswap((uint32_t)(x>>32));
|
||||
}
|
||||
#else
|
||||
static LJ_AINLINE uint64_t lj_bswap64(uint64_t x)
|
||||
{
|
||||
uint64_t r; __asm__("bswap %0" : "=r" (r) : "0" (x)); return r;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
static LJ_AINLINE uint32_t lj_bswap(uint32_t x)
|
||||
{
|
||||
return (x << 24) | ((x & 0xff00) << 8) | ((x >> 8) & 0xff00) | (x >> 24);
|
||||
}
|
||||
|
||||
static LJ_AINLINE uint64_t lj_bswap64(uint64_t x)
|
||||
{
|
||||
return (uint64_t)lj_bswap((uint32_t)(x >> 32)) |
|
||||
((uint64_t)lj_bswap((uint32_t)x) << 32);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef union __attribute__((packed)) Unaligned16 {
|
||||
uint16_t u;
|
||||
uint8_t b[2];
|
||||
} Unaligned16;
|
||||
|
||||
typedef union __attribute__((packed)) Unaligned32 {
|
||||
uint32_t u;
|
||||
uint8_t b[4];
|
||||
} Unaligned32;
|
||||
|
||||
/* Unaligned load of uint16_t. */
|
||||
static LJ_AINLINE uint16_t lj_getu16(const void *p)
|
||||
{
|
||||
return ((const Unaligned16 *)p)->u;
|
||||
}
|
||||
|
||||
/* Unaligned load of uint32_t. */
|
||||
static LJ_AINLINE uint32_t lj_getu32(const void *p)
|
||||
{
|
||||
return ((const Unaligned32 *)p)->u;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#define LJ_NORET __declspec(noreturn)
|
||||
#define LJ_ALIGN(n) __declspec(align(n))
|
||||
#define LJ_INLINE __inline
|
||||
#define LJ_AINLINE __forceinline
|
||||
#define LJ_NOINLINE __declspec(noinline)
|
||||
#if defined(_M_IX86)
|
||||
#define LJ_FASTCALL __fastcall
|
||||
#endif
|
||||
|
||||
#ifdef _M_PPC
|
||||
#pragma intrinsic(_CountLeadingZeros)
|
||||
unsigned int _CountLeadingZeros(long);
|
||||
static LJ_AINLINE uint32_t lj_fls(uint32_t x)
|
||||
{
|
||||
return _CountLeadingZeros(x) ^ 31;
|
||||
}
|
||||
#else
|
||||
#pragma intrinsic(_BitScanForward)
|
||||
#pragma intrinsic(_BitScanReverse)
|
||||
unsigned char _BitScanForward(uint32_t *, unsigned long);
|
||||
unsigned char _BitScanReverse(uint32_t *, unsigned long);
|
||||
|
||||
static LJ_AINLINE uint32_t lj_ffs(uint32_t x)
|
||||
{
|
||||
uint32_t r; _BitScanForward(&r, x); return r;
|
||||
}
|
||||
|
||||
static LJ_AINLINE uint32_t lj_fls(uint32_t x)
|
||||
{
|
||||
uint32_t r; _BitScanReverse(&r, x); return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned long _byteswap_ulong(unsigned long);
|
||||
uint64_t _byteswap_uint64(uint64_t);
|
||||
#define lj_bswap(x) (_byteswap_ulong((x)))
|
||||
#define lj_bswap64(x) (_byteswap_uint64((x)))
|
||||
|
||||
#if defined(_M_PPC) && defined(LUAJIT_NO_UNALIGNED)
|
||||
/*
|
||||
** Replacement for unaligned loads on Xbox 360. Disabled by default since it's
|
||||
** usually more costly than the occasional stall when crossing a cache-line.
|
||||
*/
|
||||
static LJ_AINLINE uint16_t lj_getu16(const void *v)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)v;
|
||||
return (uint16_t)((p[0]<<8) | p[1]);
|
||||
}
|
||||
static LJ_AINLINE uint32_t lj_getu32(const void *v)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)v;
|
||||
return (uint32_t)((p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]);
|
||||
}
|
||||
#else
|
||||
/* Unaligned loads are generally ok on x86/x64. */
|
||||
#define lj_getu16(p) (*(uint16_t *)(p))
|
||||
#define lj_getu32(p) (*(uint32_t *)(p))
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "missing defines for your compiler"
|
||||
#endif
|
||||
|
||||
/* Optional defines. */
|
||||
#ifndef LJ_FASTCALL
|
||||
#define LJ_FASTCALL
|
||||
#endif
|
||||
#ifndef LJ_NORET
|
||||
#define LJ_NORET
|
||||
#endif
|
||||
#ifndef LJ_NOAPI
|
||||
#define LJ_NOAPI extern
|
||||
#endif
|
||||
#ifndef LJ_LIKELY
|
||||
#define LJ_LIKELY(x) (x)
|
||||
#define LJ_UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
/* Attributes for internal functions. */
|
||||
#define LJ_DATA LJ_NOAPI
|
||||
#define LJ_DATADEF
|
||||
#define LJ_ASMF LJ_NOAPI
|
||||
#define LJ_FUNCA LJ_NOAPI
|
||||
#if defined(ljamalg_c)
|
||||
#define LJ_FUNC static
|
||||
#else
|
||||
#define LJ_FUNC LJ_NOAPI
|
||||
#endif
|
||||
#define LJ_FUNC_NORET LJ_FUNC LJ_NORET
|
||||
#define LJ_FUNCA_NORET LJ_FUNCA LJ_NORET
|
||||
#define LJ_ASMF_NORET LJ_ASMF LJ_NORET
|
||||
|
||||
/* Runtime assertions. */
|
||||
#ifdef lua_assert
|
||||
#define check_exp(c, e) (lua_assert(c), (e))
|
||||
#define api_check(l, e) lua_assert(e)
|
||||
#else
|
||||
#define lua_assert(c) ((void)0)
|
||||
#define check_exp(c, e) (e)
|
||||
#define api_check luai_apicheck
|
||||
#endif
|
||||
|
||||
/* Static assertions. */
|
||||
#define LJ_ASSERT_NAME2(name, line) name ## line
|
||||
#define LJ_ASSERT_NAME(line) LJ_ASSERT_NAME2(lj_assert_, line)
|
||||
#ifdef __COUNTER__
|
||||
#define LJ_STATIC_ASSERT(cond) \
|
||||
extern void LJ_ASSERT_NAME(__COUNTER__)(int STATIC_ASSERTION_FAILED[(cond)?1:-1])
|
||||
#else
|
||||
#define LJ_STATIC_ASSERT(cond) \
|
||||
extern void LJ_ASSERT_NAME(__LINE__)(int STATIC_ASSERTION_FAILED[(cond)?1:-1])
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
** Instruction dispatch handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_DISPATCH_H
|
||||
#define _LJ_DISPATCH_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_jit.h"
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_MIPS
|
||||
/* Need our own global offset table for the dreaded MIPS calling conventions. */
|
||||
#if LJ_HASJIT
|
||||
#define JITGOTDEF(_) _(lj_trace_exit) _(lj_trace_hot)
|
||||
#else
|
||||
#define JITGOTDEF(_)
|
||||
#endif
|
||||
#if LJ_HASFFI
|
||||
#define FFIGOTDEF(_) \
|
||||
_(lj_meta_equal_cd) _(lj_ccallback_enter) _(lj_ccallback_leave)
|
||||
#else
|
||||
#define FFIGOTDEF(_)
|
||||
#endif
|
||||
#define GOTDEF(_) \
|
||||
_(floor) _(ceil) _(trunc) _(log) _(log10) _(exp) _(sin) _(cos) _(tan) \
|
||||
_(asin) _(acos) _(atan) _(sinh) _(cosh) _(tanh) _(frexp) _(modf) _(atan2) \
|
||||
_(pow) _(fmod) _(ldexp) \
|
||||
_(lj_dispatch_call) _(lj_dispatch_ins) _(lj_err_throw) \
|
||||
_(lj_ffh_coroutine_wrap_err) _(lj_func_closeuv) _(lj_func_newL_gc) \
|
||||
_(lj_gc_barrieruv) _(lj_gc_step) _(lj_gc_step_fixtop) _(lj_meta_arith) \
|
||||
_(lj_meta_call) _(lj_meta_cat) _(lj_meta_comp) _(lj_meta_equal) \
|
||||
_(lj_meta_for) _(lj_meta_len) _(lj_meta_tget) _(lj_meta_tset) \
|
||||
_(lj_state_growstack) _(lj_str_fromnum) _(lj_str_fromnumber) _(lj_str_new) \
|
||||
_(lj_tab_dup) _(lj_tab_get) _(lj_tab_getinth) _(lj_tab_len) _(lj_tab_new) \
|
||||
_(lj_tab_newkey) _(lj_tab_next) _(lj_tab_reasize) \
|
||||
JITGOTDEF(_) FFIGOTDEF(_)
|
||||
|
||||
enum {
|
||||
#define GOTENUM(name) LJ_GOT_##name,
|
||||
GOTDEF(GOTENUM)
|
||||
#undef GOTENUM
|
||||
LJ_GOT__MAX
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Type of hot counter. Must match the code in the assembler VM. */
|
||||
/* 16 bits are sufficient. Only 0.0015% overhead with maximum slot penalty. */
|
||||
typedef uint16_t HotCount;
|
||||
|
||||
/* Number of hot counter hash table entries (must be a power of two). */
|
||||
#define HOTCOUNT_SIZE 64
|
||||
#define HOTCOUNT_PCMASK ((HOTCOUNT_SIZE-1)*sizeof(HotCount))
|
||||
|
||||
/* Hotcount decrements. */
|
||||
#define HOTCOUNT_LOOP 2
|
||||
#define HOTCOUNT_CALL 1
|
||||
|
||||
/* This solves a circular dependency problem -- bump as needed. Sigh. */
|
||||
#define GG_NUM_ASMFF 62
|
||||
|
||||
#define GG_LEN_DDISP (BC__MAX + GG_NUM_ASMFF)
|
||||
#define GG_LEN_SDISP BC_FUNCF
|
||||
#define GG_LEN_DISP (GG_LEN_DDISP + GG_LEN_SDISP)
|
||||
|
||||
/* Global state, main thread and extra fields are allocated together. */
|
||||
typedef struct GG_State {
|
||||
lua_State L; /* Main thread. */
|
||||
global_State g; /* Global state. */
|
||||
#if LJ_TARGET_MIPS
|
||||
ASMFunction got[LJ_GOT__MAX]; /* Global offset table. */
|
||||
#endif
|
||||
#if LJ_HASJIT
|
||||
jit_State J; /* JIT state. */
|
||||
HotCount hotcount[HOTCOUNT_SIZE]; /* Hot counters. */
|
||||
#endif
|
||||
ASMFunction dispatch[GG_LEN_DISP]; /* Instruction dispatch tables. */
|
||||
BCIns bcff[GG_NUM_ASMFF]; /* Bytecode for ASM fast functions. */
|
||||
} GG_State;
|
||||
|
||||
#define GG_OFS(field) ((int)offsetof(GG_State, field))
|
||||
#define G2GG(gl) ((GG_State *)((char *)(gl) - GG_OFS(g)))
|
||||
#define J2GG(j) ((GG_State *)((char *)(j) - GG_OFS(J)))
|
||||
#define L2GG(L) (G2GG(G(L)))
|
||||
#define J2G(J) (&J2GG(J)->g)
|
||||
#define G2J(gl) (&G2GG(gl)->J)
|
||||
#define L2J(L) (&L2GG(L)->J)
|
||||
#define GG_G2DISP (GG_OFS(dispatch) - GG_OFS(g))
|
||||
#define GG_DISP2G (GG_OFS(g) - GG_OFS(dispatch))
|
||||
#define GG_DISP2J (GG_OFS(J) - GG_OFS(dispatch))
|
||||
#define GG_DISP2HOT (GG_OFS(hotcount) - GG_OFS(dispatch))
|
||||
#define GG_DISP2STATIC (GG_LEN_DDISP*(int)sizeof(ASMFunction))
|
||||
|
||||
#define hotcount_get(gg, pc) \
|
||||
(gg)->hotcount[(u32ptr(pc)>>2) & (HOTCOUNT_SIZE-1)]
|
||||
#define hotcount_set(gg, pc, val) \
|
||||
(hotcount_get((gg), (pc)) = (HotCount)(val))
|
||||
|
||||
/* Dispatch table management. */
|
||||
LJ_FUNC void lj_dispatch_init(GG_State *GG);
|
||||
#if LJ_HASJIT
|
||||
LJ_FUNC void lj_dispatch_init_hotcount(global_State *g);
|
||||
#endif
|
||||
LJ_FUNC void lj_dispatch_update(global_State *g);
|
||||
|
||||
/* Instruction dispatch callback for hooks or when recording. */
|
||||
LJ_FUNCA void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc);
|
||||
LJ_FUNCA ASMFunction LJ_FASTCALL lj_dispatch_call(lua_State *L, const BCIns*pc);
|
||||
LJ_FUNCA void LJ_FASTCALL lj_dispatch_return(lua_State *L, const BCIns *pc);
|
||||
|
||||
#if LJ_HASFFI && !defined(_BUILDVM_H)
|
||||
/* Save/restore errno and GetLastError() around hooks, exits and recording. */
|
||||
#include <errno.h>
|
||||
#if LJ_TARGET_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#define ERRNO_SAVE int olderr = errno; DWORD oldwerr = GetLastError();
|
||||
#define ERRNO_RESTORE errno = olderr; SetLastError(oldwerr);
|
||||
#else
|
||||
#define ERRNO_SAVE int olderr = errno;
|
||||
#define ERRNO_RESTORE errno = olderr;
|
||||
#endif
|
||||
#else
|
||||
#define ERRNO_SAVE
|
||||
#define ERRNO_RESTORE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
** MIPS instruction emitter.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
/* -- Emit basic instructions --------------------------------------------- */
|
||||
|
||||
static void emit_dst(ASMState *as, MIPSIns mi, Reg rd, Reg rs, Reg rt)
|
||||
{
|
||||
*--as->mcp = mi | MIPSF_D(rd) | MIPSF_S(rs) | MIPSF_T(rt);
|
||||
}
|
||||
|
||||
static void emit_dta(ASMState *as, MIPSIns mi, Reg rd, Reg rt, uint32_t a)
|
||||
{
|
||||
*--as->mcp = mi | MIPSF_D(rd) | MIPSF_T(rt) | MIPSF_A(a);
|
||||
}
|
||||
|
||||
#define emit_ds(as, mi, rd, rs) emit_dst(as, (mi), (rd), (rs), 0)
|
||||
#define emit_tg(as, mi, rt, rg) emit_dst(as, (mi), (rg)&31, 0, (rt))
|
||||
|
||||
static void emit_tsi(ASMState *as, MIPSIns mi, Reg rt, Reg rs, int32_t i)
|
||||
{
|
||||
*--as->mcp = mi | MIPSF_T(rt) | MIPSF_S(rs) | (i & 0xffff);
|
||||
}
|
||||
|
||||
#define emit_ti(as, mi, rt, i) emit_tsi(as, (mi), (rt), 0, (i))
|
||||
#define emit_hsi(as, mi, rh, rs, i) emit_tsi(as, (mi), (rh) & 31, (rs), (i))
|
||||
|
||||
static void emit_fgh(ASMState *as, MIPSIns mi, Reg rf, Reg rg, Reg rh)
|
||||
{
|
||||
*--as->mcp = mi | MIPSF_F(rf&31) | MIPSF_G(rg&31) | MIPSF_H(rh&31);
|
||||
}
|
||||
|
||||
#define emit_fg(as, mi, rf, rg) emit_fgh(as, (mi), (rf), (rg), 0)
|
||||
|
||||
static void emit_rotr(ASMState *as, Reg dest, Reg src, Reg tmp, uint32_t shift)
|
||||
{
|
||||
if ((as->flags & JIT_F_MIPS32R2)) {
|
||||
emit_dta(as, MIPSI_ROTR, dest, src, shift);
|
||||
} else {
|
||||
emit_dst(as, MIPSI_OR, dest, dest, tmp);
|
||||
emit_dta(as, MIPSI_SLL, dest, src, (-shift)&31);
|
||||
emit_dta(as, MIPSI_SRL, tmp, src, shift);
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Emit loads/stores --------------------------------------------------- */
|
||||
|
||||
/* Prefer rematerialization of BASE/L from global_State over spills. */
|
||||
#define emit_canremat(ref) ((ref) <= REF_BASE)
|
||||
|
||||
/* Try to find a one step delta relative to another constant. */
|
||||
static int emit_kdelta1(ASMState *as, Reg t, int32_t i)
|
||||
{
|
||||
RegSet work = ~as->freeset & RSET_GPR;
|
||||
while (work) {
|
||||
Reg r = rset_picktop(work);
|
||||
IRRef ref = regcost_ref(as->cost[r]);
|
||||
lua_assert(r != t);
|
||||
if (ref < ASMREF_L) {
|
||||
int32_t delta = i - (ra_iskref(ref) ? ra_krefk(as, ref) : IR(ref)->i);
|
||||
if (checki16(delta)) {
|
||||
emit_tsi(as, MIPSI_ADDIU, t, r, delta);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
rset_clear(work, r);
|
||||
}
|
||||
return 0; /* Failed. */
|
||||
}
|
||||
|
||||
/* Load a 32 bit constant into a GPR. */
|
||||
static void emit_loadi(ASMState *as, Reg r, int32_t i)
|
||||
{
|
||||
if (checki16(i)) {
|
||||
emit_ti(as, MIPSI_LI, r, i);
|
||||
} else {
|
||||
if ((i & 0xffff)) {
|
||||
int32_t jgl = i32ptr(J2G(as->J));
|
||||
if ((uint32_t)(i-jgl) < 65536) {
|
||||
emit_tsi(as, MIPSI_ADDIU, r, RID_JGL, i-jgl-32768);
|
||||
return;
|
||||
} else if (emit_kdelta1(as, r, i)) {
|
||||
return;
|
||||
} else if ((i >> 16) == 0) {
|
||||
emit_tsi(as, MIPSI_ORI, r, RID_ZERO, i);
|
||||
return;
|
||||
}
|
||||
emit_tsi(as, MIPSI_ORI, r, r, i);
|
||||
}
|
||||
emit_ti(as, MIPSI_LUI, r, (i >> 16));
|
||||
}
|
||||
}
|
||||
|
||||
#define emit_loada(as, r, addr) emit_loadi(as, (r), i32ptr((addr)))
|
||||
|
||||
static Reg ra_allock(ASMState *as, int32_t k, RegSet allow);
|
||||
static void ra_allockreg(ASMState *as, int32_t k, Reg r);
|
||||
|
||||
/* Get/set from constant pointer. */
|
||||
static void emit_lsptr(ASMState *as, MIPSIns mi, Reg r, void *p, RegSet allow)
|
||||
{
|
||||
int32_t jgl = i32ptr(J2G(as->J));
|
||||
int32_t i = i32ptr(p);
|
||||
Reg base;
|
||||
if ((uint32_t)(i-jgl) < 65536) {
|
||||
i = i-jgl-32768;
|
||||
base = RID_JGL;
|
||||
} else {
|
||||
base = ra_allock(as, i-(int16_t)i, allow);
|
||||
}
|
||||
emit_tsi(as, mi, r, base, i);
|
||||
}
|
||||
|
||||
#define emit_loadn(as, r, tv) \
|
||||
emit_lsptr(as, MIPSI_LDC1, ((r) & 31), (void *)(tv), RSET_GPR)
|
||||
|
||||
/* Get/set global_State fields. */
|
||||
static void emit_lsglptr(ASMState *as, MIPSIns mi, Reg r, int32_t ofs)
|
||||
{
|
||||
emit_tsi(as, mi, r, RID_JGL, ofs-32768);
|
||||
}
|
||||
|
||||
#define emit_getgl(as, r, field) \
|
||||
emit_lsglptr(as, MIPSI_LW, (r), (int32_t)offsetof(global_State, field))
|
||||
#define emit_setgl(as, r, field) \
|
||||
emit_lsglptr(as, MIPSI_SW, (r), (int32_t)offsetof(global_State, field))
|
||||
|
||||
/* Trace number is determined from per-trace exit stubs. */
|
||||
#define emit_setvmstate(as, i) UNUSED(i)
|
||||
|
||||
/* -- Emit control-flow instructions -------------------------------------- */
|
||||
|
||||
/* Label for internal jumps. */
|
||||
typedef MCode *MCLabel;
|
||||
|
||||
/* Return label pointing to current PC. */
|
||||
#define emit_label(as) ((as)->mcp)
|
||||
|
||||
static void emit_branch(ASMState *as, MIPSIns mi, Reg rs, Reg rt, MCode *target)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
ptrdiff_t delta = target - p;
|
||||
lua_assert(((delta + 0x8000) >> 16) == 0);
|
||||
*--p = mi | MIPSF_S(rs) | MIPSF_T(rt) | ((uint32_t)delta & 0xffffu);
|
||||
as->mcp = p;
|
||||
}
|
||||
|
||||
static void emit_jmp(ASMState *as, MCode *target)
|
||||
{
|
||||
*--as->mcp = MIPSI_NOP;
|
||||
emit_branch(as, MIPSI_B, RID_ZERO, RID_ZERO, (target));
|
||||
}
|
||||
|
||||
static void emit_call(ASMState *as, void *target)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
*--p = MIPSI_NOP;
|
||||
if ((((uintptr_t)target ^ (uintptr_t)p) >> 28) == 0)
|
||||
*--p = MIPSI_JAL | (((uintptr_t)target >>2) & 0x03ffffffu);
|
||||
else /* Target out of range: need indirect call. */
|
||||
*--p = MIPSI_JALR | MIPSF_S(RID_CFUNCADDR);
|
||||
as->mcp = p;
|
||||
ra_allockreg(as, i32ptr(target), RID_CFUNCADDR);
|
||||
}
|
||||
|
||||
/* -- Emit generic operations --------------------------------------------- */
|
||||
|
||||
#define emit_move(as, dst, src) \
|
||||
emit_ds(as, MIPSI_MOVE, (dst), (src))
|
||||
|
||||
/* Generic move between two regs. */
|
||||
static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src)
|
||||
{
|
||||
if (dst < RID_MAX_GPR)
|
||||
emit_move(as, dst, src);
|
||||
else
|
||||
emit_fg(as, irt_isnum(ir->t) ? MIPSI_MOV_D : MIPSI_MOV_S, dst, src);
|
||||
}
|
||||
|
||||
/* Generic load of register from stack slot. */
|
||||
static void emit_spload(ASMState *as, IRIns *ir, Reg r, int32_t ofs)
|
||||
{
|
||||
if (r < RID_MAX_GPR)
|
||||
emit_tsi(as, MIPSI_LW, r, RID_SP, ofs);
|
||||
else
|
||||
emit_tsi(as, irt_isnum(ir->t) ? MIPSI_LDC1 : MIPSI_LWC1,
|
||||
(r & 31), RID_SP, ofs);
|
||||
}
|
||||
|
||||
/* Generic store of register to stack slot. */
|
||||
static void emit_spstore(ASMState *as, IRIns *ir, Reg r, int32_t ofs)
|
||||
{
|
||||
if (r < RID_MAX_GPR)
|
||||
emit_tsi(as, MIPSI_SW, r, RID_SP, ofs);
|
||||
else
|
||||
emit_tsi(as, irt_isnum(ir->t) ? MIPSI_SDC1 : MIPSI_SWC1,
|
||||
(r&31), RID_SP, ofs);
|
||||
}
|
||||
|
||||
/* Add offset to pointer. */
|
||||
static void emit_addptr(ASMState *as, Reg r, int32_t ofs)
|
||||
{
|
||||
if (ofs) {
|
||||
lua_assert(checki16(ofs));
|
||||
emit_tsi(as, MIPSI_ADDIU, r, r, ofs);
|
||||
}
|
||||
}
|
||||
|
||||
#define emit_spsub(as, ofs) emit_addptr(as, RID_SP, -(ofs))
|
||||
|
||||
@@ -1,466 +0,0 @@
|
||||
/*
|
||||
** x86/x64 instruction emitter.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
/* -- Emit basic instructions --------------------------------------------- */
|
||||
|
||||
#define MODRM(mode, r1, r2) ((MCode)((mode)+(((r1)&7)<<3)+((r2)&7)))
|
||||
|
||||
#if LJ_64
|
||||
#define REXRB(p, rr, rb) \
|
||||
{ MCode rex = 0x40 + (((rr)>>1)&4) + (((rb)>>3)&1); \
|
||||
if (rex != 0x40) *--(p) = rex; }
|
||||
#define FORCE_REX 0x200
|
||||
#define REX_64 (FORCE_REX|0x080000)
|
||||
#else
|
||||
#define REXRB(p, rr, rb) ((void)0)
|
||||
#define FORCE_REX 0
|
||||
#define REX_64 0
|
||||
#endif
|
||||
|
||||
#define emit_i8(as, i) (*--as->mcp = (MCode)(i))
|
||||
#define emit_i32(as, i) (*(int32_t *)(as->mcp-4) = (i), as->mcp -= 4)
|
||||
#define emit_u32(as, u) (*(uint32_t *)(as->mcp-4) = (u), as->mcp -= 4)
|
||||
|
||||
#define emit_x87op(as, xo) \
|
||||
(*(uint16_t *)(as->mcp-2) = (uint16_t)(xo), as->mcp -= 2)
|
||||
|
||||
/* op */
|
||||
static LJ_AINLINE MCode *emit_op(x86Op xo, Reg rr, Reg rb, Reg rx,
|
||||
MCode *p, int delta)
|
||||
{
|
||||
int n = (int8_t)xo;
|
||||
#if defined(__GNUC__)
|
||||
if (__builtin_constant_p(xo) && n == -2)
|
||||
p[delta-2] = (MCode)(xo >> 24);
|
||||
else if (__builtin_constant_p(xo) && n == -3)
|
||||
*(uint16_t *)(p+delta-3) = (uint16_t)(xo >> 16);
|
||||
else
|
||||
#endif
|
||||
*(uint32_t *)(p+delta-5) = (uint32_t)xo;
|
||||
p += n + delta;
|
||||
#if LJ_64
|
||||
{
|
||||
uint32_t rex = 0x40 + ((rr>>1)&(4+(FORCE_REX>>1)))+((rx>>2)&2)+((rb>>3)&1);
|
||||
if (rex != 0x40) {
|
||||
rex |= (rr >> 16);
|
||||
if (n == -4) { *p = (MCode)rex; rex = (MCode)(xo >> 8); }
|
||||
else if ((xo & 0xffffff) == 0x6600fd) { *p = (MCode)rex; rex = 0x66; }
|
||||
*--p = (MCode)rex;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED(rr); UNUSED(rb); UNUSED(rx);
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
/* op + modrm */
|
||||
#define emit_opm(xo, mode, rr, rb, p, delta) \
|
||||
(p[(delta)-1] = MODRM((mode), (rr), (rb)), \
|
||||
emit_op((xo), (rr), (rb), 0, (p), (delta)))
|
||||
|
||||
/* op + modrm + sib */
|
||||
#define emit_opmx(xo, mode, scale, rr, rb, rx, p) \
|
||||
(p[-1] = MODRM((scale), (rx), (rb)), \
|
||||
p[-2] = MODRM((mode), (rr), RID_ESP), \
|
||||
emit_op((xo), (rr), (rb), (rx), (p), -1))
|
||||
|
||||
/* op r1, r2 */
|
||||
static void emit_rr(ASMState *as, x86Op xo, Reg r1, Reg r2)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
as->mcp = emit_opm(xo, XM_REG, r1, r2, p, 0);
|
||||
}
|
||||
|
||||
#if LJ_64 && defined(LUA_USE_ASSERT)
|
||||
/* [addr] is sign-extended in x64 and must be in lower 2G (not 4G). */
|
||||
static int32_t ptr2addr(const void *p)
|
||||
{
|
||||
lua_assert((uintptr_t)p < (uintptr_t)0x80000000);
|
||||
return i32ptr(p);
|
||||
}
|
||||
#else
|
||||
#define ptr2addr(p) (i32ptr((p)))
|
||||
#endif
|
||||
|
||||
/* op r, [addr] */
|
||||
static void emit_rma(ASMState *as, x86Op xo, Reg rr, const void *addr)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
*(int32_t *)(p-4) = ptr2addr(addr);
|
||||
#if LJ_64
|
||||
p[-5] = MODRM(XM_SCALE1, RID_ESP, RID_EBP);
|
||||
as->mcp = emit_opm(xo, XM_OFS0, rr, RID_ESP, p, -5);
|
||||
#else
|
||||
as->mcp = emit_opm(xo, XM_OFS0, rr, RID_EBP, p, -4);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* op r, [base+ofs] */
|
||||
static void emit_rmro(ASMState *as, x86Op xo, Reg rr, Reg rb, int32_t ofs)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
x86Mode mode;
|
||||
if (ra_hasreg(rb)) {
|
||||
if (ofs == 0 && (rb&7) != RID_EBP) {
|
||||
mode = XM_OFS0;
|
||||
} else if (checki8(ofs)) {
|
||||
*--p = (MCode)ofs;
|
||||
mode = XM_OFS8;
|
||||
} else {
|
||||
p -= 4;
|
||||
*(int32_t *)p = ofs;
|
||||
mode = XM_OFS32;
|
||||
}
|
||||
if ((rb&7) == RID_ESP)
|
||||
*--p = MODRM(XM_SCALE1, RID_ESP, RID_ESP);
|
||||
} else {
|
||||
*(int32_t *)(p-4) = ofs;
|
||||
#if LJ_64
|
||||
p[-5] = MODRM(XM_SCALE1, RID_ESP, RID_EBP);
|
||||
p -= 5;
|
||||
rb = RID_ESP;
|
||||
#else
|
||||
p -= 4;
|
||||
rb = RID_EBP;
|
||||
#endif
|
||||
mode = XM_OFS0;
|
||||
}
|
||||
as->mcp = emit_opm(xo, mode, rr, rb, p, 0);
|
||||
}
|
||||
|
||||
/* op r, [base+idx*scale+ofs] */
|
||||
static void emit_rmrxo(ASMState *as, x86Op xo, Reg rr, Reg rb, Reg rx,
|
||||
x86Mode scale, int32_t ofs)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
x86Mode mode;
|
||||
if (ofs == 0 && (rb&7) != RID_EBP) {
|
||||
mode = XM_OFS0;
|
||||
} else if (checki8(ofs)) {
|
||||
mode = XM_OFS8;
|
||||
*--p = (MCode)ofs;
|
||||
} else {
|
||||
mode = XM_OFS32;
|
||||
p -= 4;
|
||||
*(int32_t *)p = ofs;
|
||||
}
|
||||
as->mcp = emit_opmx(xo, mode, scale, rr, rb, rx, p);
|
||||
}
|
||||
|
||||
/* op r, i */
|
||||
static void emit_gri(ASMState *as, x86Group xg, Reg rb, int32_t i)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
x86Op xo;
|
||||
if (checki8(i)) {
|
||||
*--p = (MCode)i;
|
||||
xo = XG_TOXOi8(xg);
|
||||
} else {
|
||||
p -= 4;
|
||||
*(int32_t *)p = i;
|
||||
xo = XG_TOXOi(xg);
|
||||
}
|
||||
as->mcp = emit_opm(xo, XM_REG, (Reg)(xg & 7) | (rb & REX_64), rb, p, 0);
|
||||
}
|
||||
|
||||
/* op [base+ofs], i */
|
||||
static void emit_gmroi(ASMState *as, x86Group xg, Reg rb, int32_t ofs,
|
||||
int32_t i)
|
||||
{
|
||||
x86Op xo;
|
||||
if (checki8(i)) {
|
||||
emit_i8(as, i);
|
||||
xo = XG_TOXOi8(xg);
|
||||
} else {
|
||||
emit_i32(as, i);
|
||||
xo = XG_TOXOi(xg);
|
||||
}
|
||||
emit_rmro(as, xo, (Reg)(xg & 7), rb, ofs);
|
||||
}
|
||||
|
||||
#define emit_shifti(as, xg, r, i) \
|
||||
(emit_i8(as, (i)), emit_rr(as, XO_SHIFTi, (Reg)(xg), (r)))
|
||||
|
||||
/* op r, rm/mrm */
|
||||
static void emit_mrm(ASMState *as, x86Op xo, Reg rr, Reg rb)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
x86Mode mode = XM_REG;
|
||||
if (rb == RID_MRM) {
|
||||
rb = as->mrm.base;
|
||||
if (rb == RID_NONE) {
|
||||
rb = RID_EBP;
|
||||
mode = XM_OFS0;
|
||||
p -= 4;
|
||||
*(int32_t *)p = as->mrm.ofs;
|
||||
if (as->mrm.idx != RID_NONE)
|
||||
goto mrmidx;
|
||||
#if LJ_64
|
||||
*--p = MODRM(XM_SCALE1, RID_ESP, RID_EBP);
|
||||
rb = RID_ESP;
|
||||
#endif
|
||||
} else {
|
||||
if (as->mrm.ofs == 0 && (rb&7) != RID_EBP) {
|
||||
mode = XM_OFS0;
|
||||
} else if (checki8(as->mrm.ofs)) {
|
||||
*--p = (MCode)as->mrm.ofs;
|
||||
mode = XM_OFS8;
|
||||
} else {
|
||||
p -= 4;
|
||||
*(int32_t *)p = as->mrm.ofs;
|
||||
mode = XM_OFS32;
|
||||
}
|
||||
if (as->mrm.idx != RID_NONE) {
|
||||
mrmidx:
|
||||
as->mcp = emit_opmx(xo, mode, as->mrm.scale, rr, rb, as->mrm.idx, p);
|
||||
return;
|
||||
}
|
||||
if ((rb&7) == RID_ESP)
|
||||
*--p = MODRM(XM_SCALE1, RID_ESP, RID_ESP);
|
||||
}
|
||||
}
|
||||
as->mcp = emit_opm(xo, mode, rr, rb, p, 0);
|
||||
}
|
||||
|
||||
/* op rm/mrm, i */
|
||||
static void emit_gmrmi(ASMState *as, x86Group xg, Reg rb, int32_t i)
|
||||
{
|
||||
x86Op xo;
|
||||
if (checki8(i)) {
|
||||
emit_i8(as, i);
|
||||
xo = XG_TOXOi8(xg);
|
||||
} else {
|
||||
emit_i32(as, i);
|
||||
xo = XG_TOXOi(xg);
|
||||
}
|
||||
emit_mrm(as, xo, (Reg)(xg & 7) | (rb & REX_64), (rb & ~REX_64));
|
||||
}
|
||||
|
||||
/* -- Emit loads/stores --------------------------------------------------- */
|
||||
|
||||
/* Instruction selection for XMM moves. */
|
||||
#define XMM_MOVRR(as) ((as->flags & JIT_F_SPLIT_XMM) ? XO_MOVSD : XO_MOVAPS)
|
||||
#define XMM_MOVRM(as) ((as->flags & JIT_F_SPLIT_XMM) ? XO_MOVLPD : XO_MOVSD)
|
||||
|
||||
/* mov [base+ofs], i */
|
||||
static void emit_movmroi(ASMState *as, Reg base, int32_t ofs, int32_t i)
|
||||
{
|
||||
emit_i32(as, i);
|
||||
emit_rmro(as, XO_MOVmi, 0, base, ofs);
|
||||
}
|
||||
|
||||
/* mov [base+ofs], r */
|
||||
#define emit_movtomro(as, r, base, ofs) \
|
||||
emit_rmro(as, XO_MOVto, (r), (base), (ofs))
|
||||
|
||||
/* Get/set global_State fields. */
|
||||
#define emit_opgl(as, xo, r, field) \
|
||||
emit_rma(as, (xo), (r), (void *)&J2G(as->J)->field)
|
||||
#define emit_getgl(as, r, field) emit_opgl(as, XO_MOV, (r), field)
|
||||
#define emit_setgl(as, r, field) emit_opgl(as, XO_MOVto, (r), field)
|
||||
|
||||
#define emit_setvmstate(as, i) \
|
||||
(emit_i32(as, i), emit_opgl(as, XO_MOVmi, 0, vmstate))
|
||||
|
||||
/* mov r, i / xor r, r */
|
||||
static void emit_loadi(ASMState *as, Reg r, int32_t i)
|
||||
{
|
||||
/* XOR r,r is shorter, but modifies the flags. This is bad for HIOP. */
|
||||
if (i == 0 && !(LJ_32 && (IR(as->curins)->o == IR_HIOP ||
|
||||
(as->curins+1 < as->T->nins &&
|
||||
IR(as->curins+1)->o == IR_HIOP)))) {
|
||||
emit_rr(as, XO_ARITH(XOg_XOR), r, r);
|
||||
} else {
|
||||
MCode *p = as->mcp;
|
||||
*(int32_t *)(p-4) = i;
|
||||
p[-5] = (MCode)(XI_MOVri+(r&7));
|
||||
p -= 5;
|
||||
REXRB(p, 0, r);
|
||||
as->mcp = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* mov r, addr */
|
||||
#define emit_loada(as, r, addr) \
|
||||
emit_loadi(as, (r), ptr2addr((addr)))
|
||||
|
||||
#if LJ_64
|
||||
/* mov r, imm64 or shorter 32 bit extended load. */
|
||||
static void emit_loadu64(ASMState *as, Reg r, uint64_t u64)
|
||||
{
|
||||
if (checku32(u64)) { /* 32 bit load clears upper 32 bits. */
|
||||
emit_loadi(as, r, (int32_t)u64);
|
||||
} else if (checki32((int64_t)u64)) { /* Sign-extended 32 bit load. */
|
||||
MCode *p = as->mcp;
|
||||
*(int32_t *)(p-4) = (int32_t)u64;
|
||||
as->mcp = emit_opm(XO_MOVmi, XM_REG, REX_64, r, p, -4);
|
||||
} else { /* Full-size 64 bit load. */
|
||||
MCode *p = as->mcp;
|
||||
*(uint64_t *)(p-8) = u64;
|
||||
p[-9] = (MCode)(XI_MOVri+(r&7));
|
||||
p[-10] = 0x48 + ((r>>3)&1);
|
||||
p -= 10;
|
||||
as->mcp = p;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* movsd r, [&tv->n] / xorps r, r */
|
||||
static void emit_loadn(ASMState *as, Reg r, cTValue *tv)
|
||||
{
|
||||
if (tvispzero(tv)) /* Use xor only for +0. */
|
||||
emit_rr(as, XO_XORPS, r, r);
|
||||
else
|
||||
emit_rma(as, XMM_MOVRM(as), r, &tv->n);
|
||||
}
|
||||
|
||||
/* -- Emit control-flow instructions -------------------------------------- */
|
||||
|
||||
/* Label for short jumps. */
|
||||
typedef MCode *MCLabel;
|
||||
|
||||
#if LJ_32 && LJ_HASFFI
|
||||
/* jmp short target */
|
||||
static void emit_sjmp(ASMState *as, MCLabel target)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
ptrdiff_t delta = target - p;
|
||||
lua_assert(delta == (int8_t)delta);
|
||||
p[-1] = (MCode)(int8_t)delta;
|
||||
p[-2] = XI_JMPs;
|
||||
as->mcp = p - 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* jcc short target */
|
||||
static void emit_sjcc(ASMState *as, int cc, MCLabel target)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
ptrdiff_t delta = target - p;
|
||||
lua_assert(delta == (int8_t)delta);
|
||||
p[-1] = (MCode)(int8_t)delta;
|
||||
p[-2] = (MCode)(XI_JCCs+(cc&15));
|
||||
as->mcp = p - 2;
|
||||
}
|
||||
|
||||
/* jcc short (pending target) */
|
||||
static MCLabel emit_sjcc_label(ASMState *as, int cc)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
p[-1] = 0;
|
||||
p[-2] = (MCode)(XI_JCCs+(cc&15));
|
||||
as->mcp = p - 2;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Fixup jcc short target. */
|
||||
static void emit_sfixup(ASMState *as, MCLabel source)
|
||||
{
|
||||
source[-1] = (MCode)(as->mcp-source);
|
||||
}
|
||||
|
||||
/* Return label pointing to current PC. */
|
||||
#define emit_label(as) ((as)->mcp)
|
||||
|
||||
/* Compute relative 32 bit offset for jump and call instructions. */
|
||||
static LJ_AINLINE int32_t jmprel(MCode *p, MCode *target)
|
||||
{
|
||||
ptrdiff_t delta = target - p;
|
||||
lua_assert(delta == (int32_t)delta);
|
||||
return (int32_t)delta;
|
||||
}
|
||||
|
||||
/* jcc target */
|
||||
static void emit_jcc(ASMState *as, int cc, MCode *target)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
*(int32_t *)(p-4) = jmprel(p, target);
|
||||
p[-5] = (MCode)(XI_JCCn+(cc&15));
|
||||
p[-6] = 0x0f;
|
||||
as->mcp = p - 6;
|
||||
}
|
||||
|
||||
/* jmp target */
|
||||
static void emit_jmp(ASMState *as, MCode *target)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
*(int32_t *)(p-4) = jmprel(p, target);
|
||||
p[-5] = XI_JMP;
|
||||
as->mcp = p - 5;
|
||||
}
|
||||
|
||||
/* call target */
|
||||
static void emit_call_(ASMState *as, MCode *target)
|
||||
{
|
||||
MCode *p = as->mcp;
|
||||
#if LJ_64
|
||||
if (target-p != (int32_t)(target-p)) {
|
||||
/* Assumes RID_RET is never an argument to calls and always clobbered. */
|
||||
emit_rr(as, XO_GROUP5, XOg_CALL, RID_RET);
|
||||
emit_loadu64(as, RID_RET, (uint64_t)target);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
*(int32_t *)(p-4) = jmprel(p, target);
|
||||
p[-5] = XI_CALL;
|
||||
as->mcp = p - 5;
|
||||
}
|
||||
|
||||
#define emit_call(as, f) emit_call_(as, (MCode *)(void *)(f))
|
||||
|
||||
/* -- Emit generic operations --------------------------------------------- */
|
||||
|
||||
/* Use 64 bit operations to handle 64 bit IR types. */
|
||||
#if LJ_64
|
||||
#define REX_64IR(ir, r) ((r) + (irt_is64((ir)->t) ? REX_64 : 0))
|
||||
#else
|
||||
#define REX_64IR(ir, r) (r)
|
||||
#endif
|
||||
|
||||
/* Generic move between two regs. */
|
||||
static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src)
|
||||
{
|
||||
UNUSED(ir);
|
||||
if (dst < RID_MAX_GPR)
|
||||
emit_rr(as, XO_MOV, REX_64IR(ir, dst), src);
|
||||
else
|
||||
emit_rr(as, XMM_MOVRR(as), dst, src);
|
||||
}
|
||||
|
||||
/* Generic load of register from stack slot. */
|
||||
static void emit_spload(ASMState *as, IRIns *ir, Reg r, int32_t ofs)
|
||||
{
|
||||
if (r < RID_MAX_GPR)
|
||||
emit_rmro(as, XO_MOV, REX_64IR(ir, r), RID_ESP, ofs);
|
||||
else
|
||||
emit_rmro(as, irt_isnum(ir->t) ? XMM_MOVRM(as) : XO_MOVSS, r, RID_ESP, ofs);
|
||||
}
|
||||
|
||||
/* Generic store of register to stack slot. */
|
||||
static void emit_spstore(ASMState *as, IRIns *ir, Reg r, int32_t ofs)
|
||||
{
|
||||
if (r < RID_MAX_GPR)
|
||||
emit_rmro(as, XO_MOVto, REX_64IR(ir, r), RID_ESP, ofs);
|
||||
else
|
||||
emit_rmro(as, irt_isnum(ir->t) ? XO_MOVSDto : XO_MOVSSto, r, RID_ESP, ofs);
|
||||
}
|
||||
|
||||
/* Add offset to pointer. */
|
||||
static void emit_addptr(ASMState *as, Reg r, int32_t ofs)
|
||||
{
|
||||
if (ofs) {
|
||||
if ((as->flags & JIT_F_LEA_AGU))
|
||||
emit_rmro(as, XO_LEA, r, r, ofs);
|
||||
else
|
||||
emit_gri(as, XG_ARITHi(XOg_ADD), r, ofs);
|
||||
}
|
||||
}
|
||||
|
||||
#define emit_spsub(as, ofs) emit_addptr(as, RID_ESP|REX_64, -(ofs))
|
||||
|
||||
/* Prefer rematerialization of BASE/L from global_State over spills. */
|
||||
#define emit_canremat(ref) ((ref) <= REF_BASE)
|
||||
|
||||
@@ -1,781 +0,0 @@
|
||||
/*
|
||||
** Error handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_err_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_func.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_vm.h"
|
||||
|
||||
/*
|
||||
** LuaJIT can either use internal or external frame unwinding:
|
||||
**
|
||||
** - Internal frame unwinding (INT) is free-standing and doesn't require
|
||||
** any OS or library support.
|
||||
**
|
||||
** - External frame unwinding (EXT) uses the system-provided unwind handler.
|
||||
**
|
||||
** Pros and Cons:
|
||||
**
|
||||
** - EXT requires unwind tables for *all* functions on the C stack between
|
||||
** the pcall/catch and the error/throw. This is the default on x64,
|
||||
** but needs to be manually enabled on x86/PPC for non-C++ code.
|
||||
**
|
||||
** - INT is faster when actually throwing errors (but this happens rarely).
|
||||
** Setting up error handlers is zero-cost in any case.
|
||||
**
|
||||
** - EXT provides full interoperability with C++ exceptions. You can throw
|
||||
** Lua errors or C++ exceptions through a mix of Lua frames and C++ frames.
|
||||
** C++ destructors are called as needed. C++ exceptions caught by pcall
|
||||
** are converted to the string "C++ exception". Lua errors can be caught
|
||||
** with catch (...) in C++.
|
||||
**
|
||||
** - INT has only limited support for automatically catching C++ exceptions
|
||||
** on POSIX systems using DWARF2 stack unwinding. Other systems may use
|
||||
** the wrapper function feature. Lua errors thrown through C++ frames
|
||||
** cannot be caught by C++ code and C++ destructors are not run.
|
||||
**
|
||||
** EXT is the default on x64 systems, INT is the default on all other systems.
|
||||
**
|
||||
** EXT can be manually enabled on POSIX systems using GCC and DWARF2 stack
|
||||
** unwinding with -DLUAJIT_UNWIND_EXTERNAL. *All* C code must be compiled
|
||||
** with -funwind-tables (or -fexceptions). This includes LuaJIT itself (set
|
||||
** TARGET_CFLAGS), all of your C/Lua binding code, all loadable C modules
|
||||
** and all C libraries that have callbacks which may be used to call back
|
||||
** into Lua. C++ code must *not* be compiled with -fno-exceptions.
|
||||
**
|
||||
** EXT cannot be enabled on WIN32 since system exceptions use code-driven SEH.
|
||||
** EXT is mandatory on WIN64 since the calling convention has an abundance
|
||||
** of callee-saved registers (rbx, rbp, rsi, rdi, r12-r15, xmm6-xmm15).
|
||||
** EXT is mandatory on POSIX/x64 since the interpreter doesn't save r12/r13.
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__) && (LJ_TARGET_X64 || defined(LUAJIT_UNWIND_EXTERNAL))
|
||||
#define LJ_UNWIND_EXT 1
|
||||
#elif LJ_TARGET_X64 && LJ_TARGET_WINDOWS
|
||||
#define LJ_UNWIND_EXT 1
|
||||
#endif
|
||||
|
||||
/* -- Error messages ------------------------------------------------------ */
|
||||
|
||||
/* Error message strings. */
|
||||
LJ_DATADEF const char *lj_err_allmsg =
|
||||
#define ERRDEF(name, msg) msg "\0"
|
||||
#include "lj_errmsg.h"
|
||||
;
|
||||
|
||||
/* -- Internal frame unwinding -------------------------------------------- */
|
||||
|
||||
/* Unwind Lua stack and move error message to new top. */
|
||||
LJ_NOINLINE static void unwindstack(lua_State *L, TValue *top)
|
||||
{
|
||||
lj_func_closeuv(L, top);
|
||||
if (top < L->top-1) {
|
||||
copyTV(L, top, L->top-1);
|
||||
L->top = top+1;
|
||||
}
|
||||
lj_state_relimitstack(L);
|
||||
}
|
||||
|
||||
/* Unwind until stop frame. Optionally cleanup frames. */
|
||||
static void *err_unwind(lua_State *L, void *stopcf, int errcode)
|
||||
{
|
||||
TValue *frame = L->base-1;
|
||||
void *cf = L->cframe;
|
||||
while (cf) {
|
||||
int32_t nres = cframe_nres(cframe_raw(cf));
|
||||
if (nres < 0) { /* C frame without Lua frame? */
|
||||
TValue *top = restorestack(L, -nres);
|
||||
if (frame < top) { /* Frame reached? */
|
||||
if (errcode) {
|
||||
L->cframe = cframe_prev(cf);
|
||||
L->base = frame+1;
|
||||
unwindstack(L, top);
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
}
|
||||
if (frame <= tvref(L->stack))
|
||||
break;
|
||||
switch (frame_typep(frame)) {
|
||||
case FRAME_LUA: /* Lua frame. */
|
||||
case FRAME_LUAP:
|
||||
frame = frame_prevl(frame);
|
||||
break;
|
||||
case FRAME_C: /* C frame. */
|
||||
#if LJ_HASFFI
|
||||
unwind_c:
|
||||
#endif
|
||||
#if LJ_UNWIND_EXT
|
||||
if (errcode) {
|
||||
L->cframe = cframe_prev(cf);
|
||||
L->base = frame_prevd(frame) + 1;
|
||||
unwindstack(L, frame);
|
||||
} else if (cf != stopcf) {
|
||||
cf = cframe_prev(cf);
|
||||
frame = frame_prevd(frame);
|
||||
break;
|
||||
}
|
||||
return NULL; /* Continue unwinding. */
|
||||
#else
|
||||
UNUSED(stopcf);
|
||||
cf = cframe_prev(cf);
|
||||
frame = frame_prevd(frame);
|
||||
break;
|
||||
#endif
|
||||
case FRAME_CP: /* Protected C frame. */
|
||||
if (cframe_canyield(cf)) { /* Resume? */
|
||||
if (errcode) {
|
||||
hook_leave(G(L)); /* Assumes nobody uses coroutines inside hooks. */
|
||||
L->cframe = NULL;
|
||||
L->status = (uint8_t)errcode;
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
if (errcode) {
|
||||
L->cframe = cframe_prev(cf);
|
||||
L->base = frame_prevd(frame) + 1;
|
||||
unwindstack(L, frame);
|
||||
}
|
||||
return cf;
|
||||
case FRAME_CONT: /* Continuation frame. */
|
||||
#if LJ_HASFFI
|
||||
if ((frame-1)->u32.lo == LJ_CONT_FFI_CALLBACK)
|
||||
goto unwind_c;
|
||||
#endif
|
||||
case FRAME_VARG: /* Vararg frame. */
|
||||
frame = frame_prevd(frame);
|
||||
break;
|
||||
case FRAME_PCALL: /* FF pcall() frame. */
|
||||
case FRAME_PCALLH: /* FF pcall() frame inside hook. */
|
||||
if (errcode) {
|
||||
if (errcode == LUA_YIELD) {
|
||||
frame = frame_prevd(frame);
|
||||
break;
|
||||
}
|
||||
if (frame_typep(frame) == FRAME_PCALL)
|
||||
hook_leave(G(L));
|
||||
L->cframe = cf;
|
||||
L->base = frame_prevd(frame) + 1;
|
||||
unwindstack(L, L->base);
|
||||
}
|
||||
return (void *)((intptr_t)cf | CFRAME_UNWIND_FF);
|
||||
}
|
||||
}
|
||||
/* No C frame. */
|
||||
if (errcode) {
|
||||
L->cframe = NULL;
|
||||
L->base = tvref(L->stack)+1;
|
||||
unwindstack(L, L->base);
|
||||
if (G(L)->panic)
|
||||
G(L)->panic(L);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return L; /* Anything non-NULL will do. */
|
||||
}
|
||||
|
||||
/* -- External frame unwinding -------------------------------------------- */
|
||||
|
||||
#if defined(__GNUC__) && !LJ_NO_UNWIND && !LJ_TARGET_WINDOWS
|
||||
|
||||
/*
|
||||
** We have to use our own definitions instead of the mandatory (!) unwind.h,
|
||||
** since various OS, distros and compilers mess up the header installation.
|
||||
*/
|
||||
|
||||
typedef struct _Unwind_Exception
|
||||
{
|
||||
uint64_t exclass;
|
||||
void (*excleanup)(int, struct _Unwind_Exception);
|
||||
uintptr_t p1, p2;
|
||||
} __attribute__((__aligned__)) _Unwind_Exception;
|
||||
|
||||
typedef struct _Unwind_Context _Unwind_Context;
|
||||
|
||||
#define _URC_OK 0
|
||||
#define _URC_FATAL_PHASE1_ERROR 3
|
||||
#define _URC_HANDLER_FOUND 6
|
||||
#define _URC_INSTALL_CONTEXT 7
|
||||
#define _URC_CONTINUE_UNWIND 8
|
||||
#define _URC_FAILURE 9
|
||||
|
||||
#if !LJ_TARGET_ARM
|
||||
|
||||
extern uintptr_t _Unwind_GetCFA(_Unwind_Context *);
|
||||
extern void _Unwind_SetGR(_Unwind_Context *, int, uintptr_t);
|
||||
extern void _Unwind_SetIP(_Unwind_Context *, uintptr_t);
|
||||
extern void _Unwind_DeleteException(_Unwind_Exception *);
|
||||
extern int _Unwind_RaiseException(_Unwind_Exception *);
|
||||
|
||||
#define _UA_SEARCH_PHASE 1
|
||||
#define _UA_CLEANUP_PHASE 2
|
||||
#define _UA_HANDLER_FRAME 4
|
||||
#define _UA_FORCE_UNWIND 8
|
||||
|
||||
#define LJ_UEXCLASS 0x4c55414a49543200ULL /* LUAJIT2\0 */
|
||||
#define LJ_UEXCLASS_MAKE(c) (LJ_UEXCLASS | (uint64_t)(c))
|
||||
#define LJ_UEXCLASS_CHECK(cl) (((cl) ^ LJ_UEXCLASS) <= 0xff)
|
||||
#define LJ_UEXCLASS_ERRCODE(cl) ((int)((cl) & 0xff))
|
||||
|
||||
/* DWARF2 personality handler referenced from interpreter .eh_frame. */
|
||||
LJ_FUNCA int lj_err_unwind_dwarf(int version, int actions,
|
||||
uint64_t uexclass, _Unwind_Exception *uex, _Unwind_Context *ctx)
|
||||
{
|
||||
void *cf;
|
||||
lua_State *L;
|
||||
if (version != 1)
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
UNUSED(uexclass);
|
||||
cf = (void *)_Unwind_GetCFA(ctx);
|
||||
L = cframe_L(cf);
|
||||
if ((actions & _UA_SEARCH_PHASE)) {
|
||||
#if LJ_UNWIND_EXT
|
||||
if (err_unwind(L, cf, 0) == NULL)
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
#endif
|
||||
if (!LJ_UEXCLASS_CHECK(uexclass)) {
|
||||
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP));
|
||||
}
|
||||
return _URC_HANDLER_FOUND;
|
||||
}
|
||||
if ((actions & _UA_CLEANUP_PHASE)) {
|
||||
int errcode;
|
||||
if (LJ_UEXCLASS_CHECK(uexclass)) {
|
||||
errcode = LJ_UEXCLASS_ERRCODE(uexclass);
|
||||
} else {
|
||||
if ((actions & _UA_HANDLER_FRAME))
|
||||
_Unwind_DeleteException(uex);
|
||||
errcode = LUA_ERRRUN;
|
||||
}
|
||||
#if LJ_UNWIND_EXT
|
||||
cf = err_unwind(L, cf, errcode);
|
||||
if ((actions & _UA_FORCE_UNWIND)) {
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
} else if (cf) {
|
||||
_Unwind_SetGR(ctx, LJ_TARGET_EHRETREG, errcode);
|
||||
_Unwind_SetIP(ctx, (uintptr_t)(cframe_unwind_ff(cf) ?
|
||||
lj_vm_unwind_ff_eh :
|
||||
lj_vm_unwind_c_eh));
|
||||
return _URC_INSTALL_CONTEXT;
|
||||
}
|
||||
#if LJ_TARGET_X86ORX64
|
||||
else if ((actions & _UA_HANDLER_FRAME)) {
|
||||
/* Workaround for ancient libgcc bug. Still present in RHEL 5.5. :-/
|
||||
** Real fix: http://gcc.gnu.org/viewcvs/trunk/gcc/unwind-dw2.c?r1=121165&r2=124837&pathrev=153877&diff_format=h
|
||||
*/
|
||||
_Unwind_SetGR(ctx, LJ_TARGET_EHRETREG, errcode);
|
||||
_Unwind_SetIP(ctx, (uintptr_t)lj_vm_unwind_rethrow);
|
||||
return _URC_INSTALL_CONTEXT;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
/* This is not the proper way to escape from the unwinder. We get away with
|
||||
** it on non-x64 because the interpreter restores all callee-saved regs.
|
||||
*/
|
||||
lj_err_throw(L, errcode);
|
||||
#endif
|
||||
}
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
}
|
||||
|
||||
#if LJ_UNWIND_EXT
|
||||
#if LJ_TARGET_OSX || defined(__OpenBSD__)
|
||||
/* Sorry, no thread safety for OSX. Complain to Apple, not me. */
|
||||
static _Unwind_Exception static_uex;
|
||||
#else
|
||||
static __thread _Unwind_Exception static_uex;
|
||||
#endif
|
||||
|
||||
/* Raise DWARF2 exception. */
|
||||
static void err_raise_ext(int errcode)
|
||||
{
|
||||
static_uex.exclass = LJ_UEXCLASS_MAKE(errcode);
|
||||
static_uex.excleanup = NULL;
|
||||
_Unwind_RaiseException(&static_uex);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
extern void _Unwind_DeleteException(void *);
|
||||
extern int __gnu_unwind_frame (void *, _Unwind_Context *);
|
||||
extern int _Unwind_VRS_Set(_Unwind_Context *, int, uint32_t, int, void *);
|
||||
extern int _Unwind_VRS_Get(_Unwind_Context *, int, uint32_t, int, void *);
|
||||
|
||||
static inline uint32_t _Unwind_GetGR(_Unwind_Context *ctx, int r)
|
||||
{
|
||||
uint32_t v;
|
||||
_Unwind_VRS_Get(ctx, 0, r, 0, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void _Unwind_SetGR(_Unwind_Context *ctx, int r, uint32_t v)
|
||||
{
|
||||
_Unwind_VRS_Set(ctx, 0, r, 0, &v);
|
||||
}
|
||||
|
||||
#define _US_VIRTUAL_UNWIND_FRAME 0
|
||||
#define _US_UNWIND_FRAME_STARTING 1
|
||||
#define _US_ACTION_MASK 3
|
||||
#define _US_FORCE_UNWIND 8
|
||||
|
||||
/* ARM unwinder personality handler referenced from interpreter .ARM.extab. */
|
||||
LJ_FUNCA int lj_err_unwind_arm(int state, void *ucb, _Unwind_Context *ctx)
|
||||
{
|
||||
void *cf = (void *)_Unwind_GetGR(ctx, 13);
|
||||
lua_State *L = cframe_L(cf);
|
||||
if ((state & _US_ACTION_MASK) == _US_VIRTUAL_UNWIND_FRAME) {
|
||||
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP));
|
||||
return _URC_HANDLER_FOUND;
|
||||
}
|
||||
if ((state&(_US_ACTION_MASK|_US_FORCE_UNWIND)) == _US_UNWIND_FRAME_STARTING) {
|
||||
_Unwind_DeleteException(ucb);
|
||||
_Unwind_SetGR(ctx, 15, (uint32_t)(void *)lj_err_throw);
|
||||
_Unwind_SetGR(ctx, 0, (uint32_t)L);
|
||||
_Unwind_SetGR(ctx, 1, (uint32_t)LUA_ERRRUN);
|
||||
return _URC_INSTALL_CONTEXT;
|
||||
}
|
||||
if (__gnu_unwind_frame(ucb, ctx) != _URC_OK)
|
||||
return _URC_FAILURE;
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#elif LJ_TARGET_X64 && LJ_TARGET_WINDOWS
|
||||
|
||||
/*
|
||||
** Someone in Redmond owes me several days of my life. A lot of this is
|
||||
** undocumented or just plain wrong on MSDN. Some of it can be gathered
|
||||
** from 3rd party docs or must be found by trial-and-error. They really
|
||||
** don't want you to write your own language-specific exception handler
|
||||
** or to interact gracefully with MSVC. :-(
|
||||
**
|
||||
** Apparently MSVC doesn't call C++ destructors for foreign exceptions
|
||||
** unless you compile your C++ code with /EHa. Unfortunately this means
|
||||
** catch (...) also catches things like access violations. The use of
|
||||
** _set_se_translator doesn't really help, because it requires /EHa, too.
|
||||
*/
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
/* Taken from: http://www.nynaeve.net/?p=99 */
|
||||
typedef struct UndocumentedDispatcherContext {
|
||||
ULONG64 ControlPc;
|
||||
ULONG64 ImageBase;
|
||||
PRUNTIME_FUNCTION FunctionEntry;
|
||||
ULONG64 EstablisherFrame;
|
||||
ULONG64 TargetIp;
|
||||
PCONTEXT ContextRecord;
|
||||
PEXCEPTION_ROUTINE LanguageHandler;
|
||||
PVOID HandlerData;
|
||||
PUNWIND_HISTORY_TABLE HistoryTable;
|
||||
ULONG ScopeIndex;
|
||||
ULONG Fill0;
|
||||
} UndocumentedDispatcherContext;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Another wild guess. */
|
||||
extern __DestructExceptionObject(EXCEPTION_RECORD *rec, int nothrow);
|
||||
#endif
|
||||
|
||||
#define LJ_MSVC_EXCODE ((DWORD)0xe06d7363)
|
||||
|
||||
#define LJ_EXCODE ((DWORD)0xe24c4a00)
|
||||
#define LJ_EXCODE_MAKE(c) (LJ_EXCODE | (DWORD)(c))
|
||||
#define LJ_EXCODE_CHECK(cl) (((cl) ^ LJ_EXCODE) <= 0xff)
|
||||
#define LJ_EXCODE_ERRCODE(cl) ((int)((cl) & 0xff))
|
||||
|
||||
/* Win64 exception handler for interpreter frame. */
|
||||
LJ_FUNCA EXCEPTION_DISPOSITION lj_err_unwind_win64(EXCEPTION_RECORD *rec,
|
||||
void *cf, CONTEXT *ctx, UndocumentedDispatcherContext *dispatch)
|
||||
{
|
||||
lua_State *L = cframe_L(cf);
|
||||
int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ?
|
||||
LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN;
|
||||
if ((rec->ExceptionFlags & 6)) { /* EH_UNWINDING|EH_EXIT_UNWIND */
|
||||
/* Unwind internal frames. */
|
||||
err_unwind(L, cf, errcode);
|
||||
} else {
|
||||
void *cf2 = err_unwind(L, cf, 0);
|
||||
if (cf2) { /* We catch it, so start unwinding the upper frames. */
|
||||
if (rec->ExceptionCode == LJ_MSVC_EXCODE) {
|
||||
#ifdef _MSC_VER
|
||||
__DestructExceptionObject(rec, 1);
|
||||
#endif
|
||||
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP));
|
||||
} else if (!LJ_EXCODE_CHECK(rec->ExceptionCode)) {
|
||||
/* Don't catch access violations etc. */
|
||||
return ExceptionContinueSearch;
|
||||
}
|
||||
/* Unwind the stack and call all handlers for all lower C frames
|
||||
** (including ourselves) again with EH_UNWINDING set. Then set
|
||||
** rsp = cf, rax = errcode and jump to the specified target.
|
||||
*/
|
||||
RtlUnwindEx(cf, (void *)((cframe_unwind_ff(cf2) && errcode != LUA_YIELD) ?
|
||||
lj_vm_unwind_ff_eh :
|
||||
lj_vm_unwind_c_eh),
|
||||
rec, (void *)errcode, ctx, dispatch->HistoryTable);
|
||||
/* RtlUnwindEx should never return. */
|
||||
}
|
||||
}
|
||||
return ExceptionContinueSearch;
|
||||
}
|
||||
|
||||
/* Raise Windows exception. */
|
||||
static void err_raise_ext(int errcode)
|
||||
{
|
||||
RaiseException(LJ_EXCODE_MAKE(errcode), 1 /* EH_NONCONTINUABLE */, 0, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* -- Error handling ------------------------------------------------------ */
|
||||
|
||||
/* Throw error. Find catch frame, unwind stack and continue. */
|
||||
LJ_NOINLINE void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode)
|
||||
{
|
||||
global_State *g = G(L);
|
||||
lj_trace_abort(g);
|
||||
setgcrefnull(g->jit_L);
|
||||
L->status = 0;
|
||||
#if LJ_UNWIND_EXT
|
||||
err_raise_ext(errcode);
|
||||
/*
|
||||
** A return from this function signals a corrupt C stack that cannot be
|
||||
** unwound. We have no choice but to call the panic function and exit.
|
||||
**
|
||||
** Usually this is caused by a C function without unwind information.
|
||||
** This should never happen on x64, but may happen if you've manually
|
||||
** enabled LUAJIT_UNWIND_EXTERNAL and forgot to recompile *every*
|
||||
** non-C++ file with -funwind-tables.
|
||||
*/
|
||||
if (G(L)->panic)
|
||||
G(L)->panic(L);
|
||||
#else
|
||||
{
|
||||
void *cf = err_unwind(L, NULL, errcode);
|
||||
if (cframe_unwind_ff(cf))
|
||||
lj_vm_unwind_ff(cframe_raw(cf));
|
||||
else
|
||||
lj_vm_unwind_c(cframe_raw(cf), errcode);
|
||||
}
|
||||
#endif
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Return string object for error message. */
|
||||
LJ_NOINLINE GCstr *lj_err_str(lua_State *L, ErrMsg em)
|
||||
{
|
||||
return lj_str_newz(L, err2msg(em));
|
||||
}
|
||||
|
||||
/* Out-of-memory error. */
|
||||
LJ_NOINLINE void lj_err_mem(lua_State *L)
|
||||
{
|
||||
if (L->status == LUA_ERRERR+1) /* Don't touch the stack during lua_open. */
|
||||
lj_vm_unwind_c(L->cframe, LUA_ERRMEM);
|
||||
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRMEM));
|
||||
lj_err_throw(L, LUA_ERRMEM);
|
||||
}
|
||||
|
||||
/* Find error function for runtime errors. Requires an extra stack traversal. */
|
||||
static ptrdiff_t finderrfunc(lua_State *L)
|
||||
{
|
||||
cTValue *frame = L->base-1, *bot = tvref(L->stack);
|
||||
void *cf = L->cframe;
|
||||
while (frame > bot) {
|
||||
lua_assert(cf != NULL);
|
||||
while (cframe_nres(cframe_raw(cf)) < 0) { /* cframe without frame? */
|
||||
if (frame >= restorestack(L, -cframe_nres(cf)))
|
||||
break;
|
||||
if (cframe_errfunc(cf) >= 0) /* Error handler not inherited (-1)? */
|
||||
return cframe_errfunc(cf);
|
||||
cf = cframe_prev(cf); /* Else unwind cframe and continue searching. */
|
||||
if (cf == NULL)
|
||||
return 0;
|
||||
}
|
||||
switch (frame_typep(frame)) {
|
||||
case FRAME_LUA:
|
||||
case FRAME_LUAP:
|
||||
frame = frame_prevl(frame);
|
||||
break;
|
||||
case FRAME_C:
|
||||
cf = cframe_prev(cf);
|
||||
/* fallthrough */
|
||||
case FRAME_CONT:
|
||||
#if LJ_HASFFI
|
||||
if ((frame-1)->u32.lo == LJ_CONT_FFI_CALLBACK)
|
||||
cf = cframe_prev(cf);
|
||||
#endif
|
||||
case FRAME_VARG:
|
||||
frame = frame_prevd(frame);
|
||||
break;
|
||||
case FRAME_CP:
|
||||
if (cframe_canyield(cf)) return 0;
|
||||
if (cframe_errfunc(cf) >= 0)
|
||||
return cframe_errfunc(cf);
|
||||
frame = frame_prevd(frame);
|
||||
break;
|
||||
case FRAME_PCALL:
|
||||
case FRAME_PCALLH:
|
||||
if (frame_ftsz(frame) >= (ptrdiff_t)(2*sizeof(TValue))) /* xpcall? */
|
||||
return savestack(L, frame-1); /* Point to xpcall's errorfunc. */
|
||||
return 0;
|
||||
default:
|
||||
lua_assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Runtime error. */
|
||||
LJ_NOINLINE void lj_err_run(lua_State *L)
|
||||
{
|
||||
ptrdiff_t ef = finderrfunc(L);
|
||||
if (ef) {
|
||||
TValue *errfunc = restorestack(L, ef);
|
||||
TValue *top = L->top;
|
||||
lj_trace_abort(G(L));
|
||||
if (!tvisfunc(errfunc) || L->status == LUA_ERRERR) {
|
||||
setstrV(L, top-1, lj_err_str(L, LJ_ERR_ERRERR));
|
||||
lj_err_throw(L, LUA_ERRERR);
|
||||
}
|
||||
L->status = LUA_ERRERR;
|
||||
copyTV(L, top, top-1);
|
||||
copyTV(L, top-1, errfunc);
|
||||
L->top = top+1;
|
||||
lj_vm_call(L, top, 1+1); /* Stack: |errfunc|msg| -> |msg| */
|
||||
}
|
||||
lj_err_throw(L, LUA_ERRRUN);
|
||||
}
|
||||
|
||||
/* Formatted runtime error message. */
|
||||
LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...)
|
||||
{
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
va_start(argp, em);
|
||||
if (curr_funcisL(L)) L->top = curr_topL(L);
|
||||
msg = lj_str_pushvf(L, err2msg(em), argp);
|
||||
va_end(argp);
|
||||
lj_debug_addloc(L, msg, L->base-1, NULL);
|
||||
lj_err_run(L);
|
||||
}
|
||||
|
||||
/* Non-vararg variant for better calling conventions. */
|
||||
LJ_NOINLINE void lj_err_msg(lua_State *L, ErrMsg em)
|
||||
{
|
||||
err_msgv(L, em);
|
||||
}
|
||||
|
||||
/* Lexer error. */
|
||||
LJ_NOINLINE void lj_err_lex(lua_State *L, GCstr *src, const char *tok,
|
||||
BCLine line, ErrMsg em, va_list argp)
|
||||
{
|
||||
char buff[LUA_IDSIZE];
|
||||
const char *msg;
|
||||
lj_debug_shortname(buff, src);
|
||||
msg = lj_str_pushvf(L, err2msg(em), argp);
|
||||
msg = lj_str_pushf(L, "%s:%d: %s", buff, line, msg);
|
||||
if (tok)
|
||||
lj_str_pushf(L, err2msg(LJ_ERR_XNEAR), msg, tok);
|
||||
lj_err_throw(L, LUA_ERRSYNTAX);
|
||||
}
|
||||
|
||||
/* Typecheck error for operands. */
|
||||
LJ_NOINLINE void lj_err_optype(lua_State *L, cTValue *o, ErrMsg opm)
|
||||
{
|
||||
const char *tname = lj_typename(o);
|
||||
const char *opname = err2msg(opm);
|
||||
if (curr_funcisL(L)) {
|
||||
GCproto *pt = curr_proto(L);
|
||||
const BCIns *pc = cframe_Lpc(L) - 1;
|
||||
const char *oname = NULL;
|
||||
const char *kind = lj_debug_slotname(pt, pc, (BCReg)(o-L->base), &oname);
|
||||
if (kind)
|
||||
err_msgv(L, LJ_ERR_BADOPRT, opname, kind, oname, tname);
|
||||
}
|
||||
err_msgv(L, LJ_ERR_BADOPRV, opname, tname);
|
||||
}
|
||||
|
||||
/* Typecheck error for ordered comparisons. */
|
||||
LJ_NOINLINE void lj_err_comp(lua_State *L, cTValue *o1, cTValue *o2)
|
||||
{
|
||||
const char *t1 = lj_typename(o1);
|
||||
const char *t2 = lj_typename(o2);
|
||||
err_msgv(L, t1 == t2 ? LJ_ERR_BADCMPV : LJ_ERR_BADCMPT, t1, t2);
|
||||
/* This assumes the two "boolean" entries are commoned by the C compiler. */
|
||||
}
|
||||
|
||||
/* Typecheck error for __call. */
|
||||
LJ_NOINLINE void lj_err_optype_call(lua_State *L, TValue *o)
|
||||
{
|
||||
/* Gross hack if lua_[p]call or pcall/xpcall fail for a non-callable object:
|
||||
** L->base still points to the caller. So add a dummy frame with L instead
|
||||
** of a function. See lua_getstack().
|
||||
*/
|
||||
const BCIns *pc = cframe_Lpc(L);
|
||||
if (((ptrdiff_t)pc & FRAME_TYPE) != FRAME_LUA) {
|
||||
const char *tname = lj_typename(o);
|
||||
setframe_pc(o, pc);
|
||||
setframe_gc(o, obj2gco(L));
|
||||
L->top = L->base = o+1;
|
||||
err_msgv(L, LJ_ERR_BADCALL, tname);
|
||||
}
|
||||
lj_err_optype(L, o, LJ_ERR_OPCALL);
|
||||
}
|
||||
|
||||
/* Error in context of caller. */
|
||||
LJ_NOINLINE void lj_err_callermsg(lua_State *L, const char *msg)
|
||||
{
|
||||
TValue *frame = L->base-1;
|
||||
TValue *pframe = NULL;
|
||||
if (frame_islua(frame)) {
|
||||
pframe = frame_prevl(frame);
|
||||
} else if (frame_iscont(frame)) {
|
||||
#if LJ_HASFFI
|
||||
if ((frame-1)->u32.lo == LJ_CONT_FFI_CALLBACK) {
|
||||
pframe = frame;
|
||||
frame = NULL;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
pframe = frame_prevd(frame);
|
||||
#if LJ_HASFFI
|
||||
/* Remove frame for FFI metamethods. */
|
||||
if (frame_func(frame)->c.ffid >= FF_ffi_meta___index &&
|
||||
frame_func(frame)->c.ffid <= FF_ffi_meta___tostring) {
|
||||
L->base = pframe+1;
|
||||
L->top = frame;
|
||||
setcframe_pc(cframe_raw(L->cframe), frame_contpc(frame));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
lj_debug_addloc(L, msg, pframe, frame);
|
||||
lj_err_run(L);
|
||||
}
|
||||
|
||||
/* Formatted error in context of caller. */
|
||||
LJ_NOINLINE void lj_err_callerv(lua_State *L, ErrMsg em, ...)
|
||||
{
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
va_start(argp, em);
|
||||
msg = lj_str_pushvf(L, err2msg(em), argp);
|
||||
va_end(argp);
|
||||
lj_err_callermsg(L, msg);
|
||||
}
|
||||
|
||||
/* Error in context of caller. */
|
||||
LJ_NOINLINE void lj_err_caller(lua_State *L, ErrMsg em)
|
||||
{
|
||||
lj_err_callermsg(L, err2msg(em));
|
||||
}
|
||||
|
||||
/* Argument error message. */
|
||||
LJ_NORET LJ_NOINLINE static void err_argmsg(lua_State *L, int narg,
|
||||
const char *msg)
|
||||
{
|
||||
const char *fname = "?";
|
||||
const char *ftype = lj_debug_funcname(L, L->base - 1, &fname);
|
||||
if (narg < 0 && narg > LUA_REGISTRYINDEX)
|
||||
narg = (int)(L->top - L->base) + narg + 1;
|
||||
if (ftype && ftype[3] == 'h' && --narg == 0) /* Check for "method". */
|
||||
msg = lj_str_pushf(L, err2msg(LJ_ERR_BADSELF), fname, msg);
|
||||
else
|
||||
msg = lj_str_pushf(L, err2msg(LJ_ERR_BADARG), narg, fname, msg);
|
||||
lj_err_callermsg(L, msg);
|
||||
}
|
||||
|
||||
/* Formatted argument error. */
|
||||
LJ_NOINLINE void lj_err_argv(lua_State *L, int narg, ErrMsg em, ...)
|
||||
{
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
va_start(argp, em);
|
||||
msg = lj_str_pushvf(L, err2msg(em), argp);
|
||||
va_end(argp);
|
||||
err_argmsg(L, narg, msg);
|
||||
}
|
||||
|
||||
/* Argument error. */
|
||||
LJ_NOINLINE void lj_err_arg(lua_State *L, int narg, ErrMsg em)
|
||||
{
|
||||
err_argmsg(L, narg, err2msg(em));
|
||||
}
|
||||
|
||||
/* Typecheck error for arguments. */
|
||||
LJ_NOINLINE void lj_err_argtype(lua_State *L, int narg, const char *xname)
|
||||
{
|
||||
TValue *o = narg < 0 ? L->top + narg : L->base + narg-1;
|
||||
const char *tname = o < L->top ? lj_typename(o) : lj_obj_typename[0];
|
||||
const char *msg = lj_str_pushf(L, err2msg(LJ_ERR_BADTYPE), xname, tname);
|
||||
err_argmsg(L, narg, msg);
|
||||
}
|
||||
|
||||
/* Typecheck error for arguments. */
|
||||
LJ_NOINLINE void lj_err_argt(lua_State *L, int narg, int tt)
|
||||
{
|
||||
lj_err_argtype(L, narg, lj_obj_typename[tt+1]);
|
||||
}
|
||||
|
||||
/* -- Public error handling API ------------------------------------------- */
|
||||
|
||||
LUA_API lua_CFunction lua_atpanic(lua_State *L, lua_CFunction panicf)
|
||||
{
|
||||
lua_CFunction old = G(L)->panic;
|
||||
G(L)->panic = panicf;
|
||||
return old;
|
||||
}
|
||||
|
||||
/* Forwarders for the public API (C calling convention and no LJ_NORET). */
|
||||
LUA_API int lua_error(lua_State *L)
|
||||
{
|
||||
lj_err_run(L);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_argerror(lua_State *L, int narg, const char *msg)
|
||||
{
|
||||
err_argmsg(L, narg, msg);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_typerror(lua_State *L, int narg, const char *xname)
|
||||
{
|
||||
lj_err_argtype(L, narg, xname);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_where(lua_State *L, int level)
|
||||
{
|
||||
int size;
|
||||
cTValue *frame = lj_debug_frame(L, level, &size);
|
||||
lj_debug_addloc(L, "", frame, size ? frame+size : NULL);
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_error(lua_State *L, const char *fmt, ...)
|
||||
{
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
msg = lj_str_pushvf(L, fmt, argp);
|
||||
va_end(argp);
|
||||
lj_err_callermsg(L, msg);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
** Error handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_ERR_H
|
||||
#define _LJ_ERR_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
typedef enum {
|
||||
#define ERRDEF(name, msg) \
|
||||
LJ_ERR_##name, LJ_ERR_##name##_ = LJ_ERR_##name + sizeof(msg)-1,
|
||||
#include "lj_errmsg.h"
|
||||
LJ_ERR__MAX
|
||||
} ErrMsg;
|
||||
|
||||
LJ_DATA const char *lj_err_allmsg;
|
||||
#define err2msg(em) (lj_err_allmsg+(int)(em))
|
||||
|
||||
LJ_FUNC GCstr *lj_err_str(lua_State *L, ErrMsg em);
|
||||
LJ_FUNCA_NORET void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode);
|
||||
LJ_FUNC_NORET void lj_err_mem(lua_State *L);
|
||||
LJ_FUNC_NORET void lj_err_run(lua_State *L);
|
||||
LJ_FUNC_NORET void lj_err_msg(lua_State *L, ErrMsg em);
|
||||
LJ_FUNC_NORET void lj_err_lex(lua_State *L, GCstr *src, const char *tok,
|
||||
BCLine line, ErrMsg em, va_list argp);
|
||||
LJ_FUNC_NORET void lj_err_optype(lua_State *L, cTValue *o, ErrMsg opm);
|
||||
LJ_FUNC_NORET void lj_err_comp(lua_State *L, cTValue *o1, cTValue *o2);
|
||||
LJ_FUNC_NORET void lj_err_optype_call(lua_State *L, TValue *o);
|
||||
LJ_FUNC_NORET void lj_err_callermsg(lua_State *L, const char *msg);
|
||||
LJ_FUNC_NORET void lj_err_callerv(lua_State *L, ErrMsg em, ...);
|
||||
LJ_FUNC_NORET void lj_err_caller(lua_State *L, ErrMsg em);
|
||||
LJ_FUNC_NORET void lj_err_arg(lua_State *L, int narg, ErrMsg em);
|
||||
LJ_FUNC_NORET void lj_err_argv(lua_State *L, int narg, ErrMsg em, ...);
|
||||
LJ_FUNC_NORET void lj_err_argtype(lua_State *L, int narg, const char *xname);
|
||||
LJ_FUNC_NORET void lj_err_argt(lua_State *L, int narg, int tt);
|
||||
|
||||
#endif
|
||||
@@ -1,889 +0,0 @@
|
||||
/*
|
||||
** Fast function call recorder.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_ffrecord_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASJIT
|
||||
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_frame.h"
|
||||
#include "lj_bc.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_ir.h"
|
||||
#include "lj_jit.h"
|
||||
#include "lj_ircall.h"
|
||||
#include "lj_iropt.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_record.h"
|
||||
#include "lj_ffrecord.h"
|
||||
#include "lj_crecord.h"
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_vm.h"
|
||||
#include "lj_strscan.h"
|
||||
|
||||
/* Some local macros to save typing. Undef'd at the end. */
|
||||
#define IR(ref) (&J->cur.ir[(ref)])
|
||||
|
||||
/* Pass IR on to next optimization in chain (FOLD). */
|
||||
#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J))
|
||||
|
||||
/* -- Fast function recording handlers ------------------------------------ */
|
||||
|
||||
/* Conventions for fast function call handlers:
|
||||
**
|
||||
** The argument slots start at J->base[0]. All of them are guaranteed to be
|
||||
** valid and type-specialized references. J->base[J->maxslot] is set to 0
|
||||
** as a sentinel. The runtime argument values start at rd->argv[0].
|
||||
**
|
||||
** In general fast functions should check for presence of all of their
|
||||
** arguments and for the correct argument types. Some simplifications
|
||||
** are allowed if the interpreter throws instead. But even if recording
|
||||
** is aborted, the generated IR must be consistent (no zero-refs).
|
||||
**
|
||||
** The number of results in rd->nres is set to 1. Handlers that return
|
||||
** a different number of results need to override it. A negative value
|
||||
** prevents return processing (e.g. for pending calls).
|
||||
**
|
||||
** Results need to be stored starting at J->base[0]. Return processing
|
||||
** moves them to the right slots later.
|
||||
**
|
||||
** The per-ffid auxiliary data is the value of the 2nd part of the
|
||||
** LJLIB_REC() annotation. This allows handling similar functionality
|
||||
** in a common handler.
|
||||
*/
|
||||
|
||||
/* Type of handler to record a fast function. */
|
||||
typedef void (LJ_FASTCALL *RecordFunc)(jit_State *J, RecordFFData *rd);
|
||||
|
||||
/* Get runtime value of int argument. */
|
||||
static int32_t argv2int(jit_State *J, TValue *o)
|
||||
{
|
||||
if (!lj_strscan_numberobj(o))
|
||||
lj_trace_err(J, LJ_TRERR_BADTYPE);
|
||||
return tvisint(o) ? intV(o) : lj_num2int(numV(o));
|
||||
}
|
||||
|
||||
/* Get runtime value of string argument. */
|
||||
static GCstr *argv2str(jit_State *J, TValue *o)
|
||||
{
|
||||
if (LJ_LIKELY(tvisstr(o))) {
|
||||
return strV(o);
|
||||
} else {
|
||||
GCstr *s;
|
||||
if (!tvisnumber(o))
|
||||
lj_trace_err(J, LJ_TRERR_BADTYPE);
|
||||
if (tvisint(o))
|
||||
s = lj_str_fromint(J->L, intV(o));
|
||||
else
|
||||
s = lj_str_fromnum(J->L, &o->n);
|
||||
setstrV(J->L, o, s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return number of results wanted by caller. */
|
||||
static ptrdiff_t results_wanted(jit_State *J)
|
||||
{
|
||||
TValue *frame = J->L->base-1;
|
||||
if (frame_islua(frame))
|
||||
return (ptrdiff_t)bc_b(frame_pc(frame)[-1]) - 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Throw error for unsupported variant of fast function. */
|
||||
LJ_NORET static void recff_nyiu(jit_State *J)
|
||||
{
|
||||
setfuncV(J->L, &J->errinfo, J->fn);
|
||||
lj_trace_err_info(J, LJ_TRERR_NYIFFU);
|
||||
}
|
||||
|
||||
/* Fallback handler for all fast functions that are not recorded (yet). */
|
||||
static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
setfuncV(J->L, &J->errinfo, J->fn);
|
||||
lj_trace_err_info(J, LJ_TRERR_NYIFF);
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* C functions can have arbitrary side-effects and are not recorded (yet). */
|
||||
static void LJ_FASTCALL recff_c(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
setfuncV(J->L, &J->errinfo, J->fn);
|
||||
lj_trace_err_info(J, LJ_TRERR_NYICF);
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* -- Base library fast functions ----------------------------------------- */
|
||||
|
||||
static void LJ_FASTCALL recff_assert(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
/* Arguments already specialized. The interpreter throws for nil/false. */
|
||||
rd->nres = J->maxslot; /* Pass through all arguments. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_type(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
/* Arguments already specialized. Result is a constant string. Neat, huh? */
|
||||
uint32_t t;
|
||||
if (tvisnumber(&rd->argv[0]))
|
||||
t = ~LJ_TNUMX;
|
||||
else if (LJ_64 && tvislightud(&rd->argv[0]))
|
||||
t = ~LJ_TLIGHTUD;
|
||||
else
|
||||
t = ~itype(&rd->argv[0]);
|
||||
J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[t]));
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_getmetatable(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
if (tr) {
|
||||
RecordIndex ix;
|
||||
ix.tab = tr;
|
||||
copyTV(J->L, &ix.tabv, &rd->argv[0]);
|
||||
if (lj_record_mm_lookup(J, &ix, MM_metatable))
|
||||
J->base[0] = ix.mobj;
|
||||
else
|
||||
J->base[0] = ix.mt;
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_setmetatable(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
TRef mt = J->base[1];
|
||||
if (tref_istab(tr) && (tref_istab(mt) || (mt && tref_isnil(mt)))) {
|
||||
TRef fref, mtref;
|
||||
RecordIndex ix;
|
||||
ix.tab = tr;
|
||||
copyTV(J->L, &ix.tabv, &rd->argv[0]);
|
||||
lj_record_mm_lookup(J, &ix, MM_metatable); /* Guard for no __metatable. */
|
||||
fref = emitir(IRT(IR_FREF, IRT_P32), tr, IRFL_TAB_META);
|
||||
mtref = tref_isnil(mt) ? lj_ir_knull(J, IRT_TAB) : mt;
|
||||
emitir(IRT(IR_FSTORE, IRT_TAB), fref, mtref);
|
||||
if (!tref_isnil(mt))
|
||||
emitir(IRT(IR_TBAR, IRT_TAB), tr, 0);
|
||||
J->base[0] = tr;
|
||||
J->needsnap = 1;
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_rawget(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
RecordIndex ix;
|
||||
ix.tab = J->base[0]; ix.key = J->base[1];
|
||||
if (tref_istab(ix.tab) && ix.key) {
|
||||
ix.val = 0; ix.idxchain = 0;
|
||||
settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
|
||||
copyTV(J->L, &ix.keyv, &rd->argv[1]);
|
||||
J->base[0] = lj_record_idx(J, &ix);
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_rawset(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
RecordIndex ix;
|
||||
ix.tab = J->base[0]; ix.key = J->base[1]; ix.val = J->base[2];
|
||||
if (tref_istab(ix.tab) && ix.key && ix.val) {
|
||||
ix.idxchain = 0;
|
||||
settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
|
||||
copyTV(J->L, &ix.keyv, &rd->argv[1]);
|
||||
copyTV(J->L, &ix.valv, &rd->argv[2]);
|
||||
lj_record_idx(J, &ix);
|
||||
/* Pass through table at J->base[0] as result. */
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_rawequal(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tra = J->base[0];
|
||||
TRef trb = J->base[1];
|
||||
if (tra && trb) {
|
||||
int diff = lj_record_objcmp(J, tra, trb, &rd->argv[0], &rd->argv[1]);
|
||||
J->base[0] = diff ? TREF_FALSE : TREF_TRUE;
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
static void LJ_FASTCALL recff_rawlen(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
if (tref_isstr(tr))
|
||||
J->base[0] = emitir(IRTI(IR_FLOAD), tr, IRFL_STR_LEN);
|
||||
else if (tref_istab(tr))
|
||||
J->base[0] = lj_ir_call(J, IRCALL_lj_tab_len, tr);
|
||||
/* else: Interpreter will throw. */
|
||||
UNUSED(rd);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Determine mode of select() call. */
|
||||
int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv)
|
||||
{
|
||||
if (tref_isstr(tr) && *strVdata(tv) == '#') { /* select('#', ...) */
|
||||
if (strV(tv)->len == 1) {
|
||||
emitir(IRTG(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, strV(tv)));
|
||||
} else {
|
||||
TRef trptr = emitir(IRT(IR_STRREF, IRT_P32), tr, lj_ir_kint(J, 0));
|
||||
TRef trchar = emitir(IRT(IR_XLOAD, IRT_U8), trptr, IRXLOAD_READONLY);
|
||||
emitir(IRTG(IR_EQ, IRT_INT), trchar, lj_ir_kint(J, '#'));
|
||||
}
|
||||
return 0;
|
||||
} else { /* select(n, ...) */
|
||||
int32_t start = argv2int(J, tv);
|
||||
if (start == 0) lj_trace_err(J, LJ_TRERR_BADTYPE); /* A bit misleading. */
|
||||
return start;
|
||||
}
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_select(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
if (tr) {
|
||||
ptrdiff_t start = lj_ffrecord_select_mode(J, tr, &rd->argv[0]);
|
||||
if (start == 0) { /* select('#', ...) */
|
||||
J->base[0] = lj_ir_kint(J, J->maxslot - 1);
|
||||
} else if (tref_isk(tr)) { /* select(k, ...) */
|
||||
ptrdiff_t n = (ptrdiff_t)J->maxslot;
|
||||
if (start < 0) start += n;
|
||||
else if (start > n) start = n;
|
||||
rd->nres = n - start;
|
||||
if (start >= 1) {
|
||||
ptrdiff_t i;
|
||||
for (i = 0; i < n - start; i++)
|
||||
J->base[i] = J->base[start+i];
|
||||
} /* else: Interpreter will throw. */
|
||||
} else {
|
||||
recff_nyiu(J);
|
||||
}
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_tonumber(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
TRef base = J->base[1];
|
||||
if (tr && base) {
|
||||
base = lj_opt_narrow_toint(J, base);
|
||||
if (!tref_isk(base) || IR(tref_ref(base))->i != 10)
|
||||
recff_nyiu(J);
|
||||
}
|
||||
if (tref_isnumber_str(tr)) {
|
||||
if (tref_isstr(tr)) {
|
||||
TValue tmp;
|
||||
if (!lj_strscan_num(strV(&rd->argv[0]), &tmp))
|
||||
recff_nyiu(J); /* Would need an inverted STRTO for this case. */
|
||||
tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0);
|
||||
}
|
||||
#if LJ_HASFFI
|
||||
} else if (tref_iscdata(tr)) {
|
||||
lj_crecord_tonumber(J, rd);
|
||||
return;
|
||||
#endif
|
||||
} else {
|
||||
tr = TREF_NIL;
|
||||
}
|
||||
J->base[0] = tr;
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
static TValue *recff_metacall_cp(lua_State *L, lua_CFunction dummy, void *ud)
|
||||
{
|
||||
jit_State *J = (jit_State *)ud;
|
||||
lj_record_tailcall(J, 0, 1);
|
||||
UNUSED(L); UNUSED(dummy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int recff_metacall(jit_State *J, RecordFFData *rd, MMS mm)
|
||||
{
|
||||
RecordIndex ix;
|
||||
ix.tab = J->base[0];
|
||||
copyTV(J->L, &ix.tabv, &rd->argv[0]);
|
||||
if (lj_record_mm_lookup(J, &ix, mm)) { /* Has metamethod? */
|
||||
int errcode;
|
||||
TValue argv0;
|
||||
/* Temporarily insert metamethod below object. */
|
||||
J->base[1] = J->base[0];
|
||||
J->base[0] = ix.mobj;
|
||||
copyTV(J->L, &argv0, &rd->argv[0]);
|
||||
copyTV(J->L, &rd->argv[1], &rd->argv[0]);
|
||||
copyTV(J->L, &rd->argv[0], &ix.mobjv);
|
||||
/* Need to protect lj_record_tailcall because it may throw. */
|
||||
errcode = lj_vm_cpcall(J->L, NULL, J, recff_metacall_cp);
|
||||
/* Always undo Lua stack changes to avoid confusing the interpreter. */
|
||||
copyTV(J->L, &rd->argv[0], &argv0);
|
||||
if (errcode)
|
||||
lj_err_throw(J->L, errcode); /* Propagate errors. */
|
||||
rd->nres = -1; /* Pending call. */
|
||||
return 1; /* Tailcalled to metamethod. */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_tostring(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
if (tref_isstr(tr)) {
|
||||
/* Ignore __tostring in the string base metatable. */
|
||||
/* Pass on result in J->base[0]. */
|
||||
} else if (!recff_metacall(J, rd, MM_tostring)) {
|
||||
if (tref_isnumber(tr)) {
|
||||
J->base[0] = emitir(IRT(IR_TOSTR, IRT_STR), tr, 0);
|
||||
} else if (tref_ispri(tr)) {
|
||||
J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[tref_type(tr)]));
|
||||
} else {
|
||||
recff_nyiu(J);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_ipairs_aux(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
RecordIndex ix;
|
||||
ix.tab = J->base[0];
|
||||
if (tref_istab(ix.tab)) {
|
||||
if (!tvisnumber(&rd->argv[1])) /* No support for string coercion. */
|
||||
lj_trace_err(J, LJ_TRERR_BADTYPE);
|
||||
setintV(&ix.keyv, numberVint(&rd->argv[1])+1);
|
||||
settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
|
||||
ix.val = 0; ix.idxchain = 0;
|
||||
ix.key = lj_opt_narrow_toint(J, J->base[1]);
|
||||
J->base[0] = ix.key = emitir(IRTI(IR_ADD), ix.key, lj_ir_kint(J, 1));
|
||||
J->base[1] = lj_record_idx(J, &ix);
|
||||
rd->nres = tref_isnil(J->base[1]) ? 0 : 2;
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_ipairs(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
if (!(LJ_52 && recff_metacall(J, rd, MM_ipairs))) {
|
||||
TRef tab = J->base[0];
|
||||
if (tref_istab(tab)) {
|
||||
J->base[0] = lj_ir_kfunc(J, funcV(&J->fn->c.upvalue[0]));
|
||||
J->base[1] = tab;
|
||||
J->base[2] = lj_ir_kint(J, 0);
|
||||
rd->nres = 3;
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_pcall(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
if (J->maxslot >= 1) {
|
||||
lj_record_call(J, 0, J->maxslot - 1);
|
||||
rd->nres = -1; /* Pending call. */
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static TValue *recff_xpcall_cp(lua_State *L, lua_CFunction dummy, void *ud)
|
||||
{
|
||||
jit_State *J = (jit_State *)ud;
|
||||
lj_record_call(J, 1, J->maxslot - 2);
|
||||
UNUSED(L); UNUSED(dummy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_xpcall(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
if (J->maxslot >= 2) {
|
||||
TValue argv0, argv1;
|
||||
TRef tmp;
|
||||
int errcode;
|
||||
/* Swap function and traceback. */
|
||||
tmp = J->base[0]; J->base[0] = J->base[1]; J->base[1] = tmp;
|
||||
copyTV(J->L, &argv0, &rd->argv[0]);
|
||||
copyTV(J->L, &argv1, &rd->argv[1]);
|
||||
copyTV(J->L, &rd->argv[0], &argv1);
|
||||
copyTV(J->L, &rd->argv[1], &argv0);
|
||||
/* Need to protect lj_record_call because it may throw. */
|
||||
errcode = lj_vm_cpcall(J->L, NULL, J, recff_xpcall_cp);
|
||||
/* Always undo Lua stack swap to avoid confusing the interpreter. */
|
||||
copyTV(J->L, &rd->argv[0], &argv0);
|
||||
copyTV(J->L, &rd->argv[1], &argv1);
|
||||
if (errcode)
|
||||
lj_err_throw(J->L, errcode); /* Propagate errors. */
|
||||
rd->nres = -1; /* Pending call. */
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
/* -- Math library fast functions ----------------------------------------- */
|
||||
|
||||
static void LJ_FASTCALL recff_math_abs(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonum(J, J->base[0]);
|
||||
J->base[0] = emitir(IRTN(IR_ABS), tr, lj_ir_knum_abs(J));
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* Record rounding functions math.floor and math.ceil. */
|
||||
static void LJ_FASTCALL recff_math_round(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
if (!tref_isinteger(tr)) { /* Pass through integers unmodified. */
|
||||
tr = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, tr), rd->data);
|
||||
/* Result is integral (or NaN/Inf), but may not fit an int32_t. */
|
||||
if (LJ_DUALNUM) { /* Try to narrow using a guarded conversion to int. */
|
||||
lua_Number n = lj_vm_foldfpm(numberVnum(&rd->argv[0]), rd->data);
|
||||
if (n == (lua_Number)lj_num2int(n))
|
||||
tr = emitir(IRTGI(IR_CONV), tr, IRCONV_INT_NUM|IRCONV_CHECK);
|
||||
}
|
||||
J->base[0] = tr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Record unary math.* functions, mapped to IR_FPMATH opcode. */
|
||||
static void LJ_FASTCALL recff_math_unary(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data);
|
||||
}
|
||||
|
||||
/* Record math.log. */
|
||||
static void LJ_FASTCALL recff_math_log(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonum(J, J->base[0]);
|
||||
if (J->base[1]) {
|
||||
#ifdef LUAJIT_NO_LOG2
|
||||
uint32_t fpm = IRFPM_LOG;
|
||||
#else
|
||||
uint32_t fpm = IRFPM_LOG2;
|
||||
#endif
|
||||
TRef trb = lj_ir_tonum(J, J->base[1]);
|
||||
tr = emitir(IRTN(IR_FPMATH), tr, fpm);
|
||||
trb = emitir(IRTN(IR_FPMATH), trb, fpm);
|
||||
trb = emitir(IRTN(IR_DIV), lj_ir_knum_one(J), trb);
|
||||
tr = emitir(IRTN(IR_MUL), tr, trb);
|
||||
} else {
|
||||
tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_LOG);
|
||||
}
|
||||
J->base[0] = tr;
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* Record math.atan2. */
|
||||
static void LJ_FASTCALL recff_math_atan2(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonum(J, J->base[0]);
|
||||
TRef tr2 = lj_ir_tonum(J, J->base[1]);
|
||||
J->base[0] = emitir(IRTN(IR_ATAN2), tr, tr2);
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* Record math.ldexp. */
|
||||
static void LJ_FASTCALL recff_math_ldexp(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonum(J, J->base[0]);
|
||||
#if LJ_TARGET_X86ORX64
|
||||
TRef tr2 = lj_ir_tonum(J, J->base[1]);
|
||||
#else
|
||||
TRef tr2 = lj_opt_narrow_toint(J, J->base[1]);
|
||||
#endif
|
||||
J->base[0] = emitir(IRTN(IR_LDEXP), tr, tr2);
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* Record math.asin, math.acos, math.atan. */
|
||||
static void LJ_FASTCALL recff_math_atrig(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef y = lj_ir_tonum(J, J->base[0]);
|
||||
TRef x = lj_ir_knum_one(J);
|
||||
uint32_t ffid = rd->data;
|
||||
if (ffid != FF_math_atan) {
|
||||
TRef tmp = emitir(IRTN(IR_MUL), y, y);
|
||||
tmp = emitir(IRTN(IR_SUB), x, tmp);
|
||||
tmp = emitir(IRTN(IR_FPMATH), tmp, IRFPM_SQRT);
|
||||
if (ffid == FF_math_asin) { x = tmp; } else { x = y; y = tmp; }
|
||||
}
|
||||
J->base[0] = emitir(IRTN(IR_ATAN2), y, x);
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_math_htrig(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonum(J, J->base[0]);
|
||||
J->base[0] = emitir(IRTN(IR_CALLN), tr, rd->data);
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_math_modf(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = J->base[0];
|
||||
if (tref_isinteger(tr)) {
|
||||
J->base[0] = tr;
|
||||
J->base[1] = lj_ir_kint(J, 0);
|
||||
} else {
|
||||
TRef trt;
|
||||
tr = lj_ir_tonum(J, tr);
|
||||
trt = emitir(IRTN(IR_FPMATH), tr, IRFPM_TRUNC);
|
||||
J->base[0] = trt;
|
||||
J->base[1] = emitir(IRTN(IR_SUB), tr, trt);
|
||||
}
|
||||
rd->nres = 2;
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_math_degrad(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonum(J, J->base[0]);
|
||||
TRef trm = lj_ir_knum(J, numV(&J->fn->c.upvalue[0]));
|
||||
J->base[0] = emitir(IRTN(IR_MUL), tr, trm);
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_math_pow(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonum(J, J->base[0]);
|
||||
if (!tref_isnumber_str(J->base[1]))
|
||||
lj_trace_err(J, LJ_TRERR_BADTYPE);
|
||||
J->base[0] = lj_opt_narrow_pow(J, tr, J->base[1], &rd->argv[1]);
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_math_minmax(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_ir_tonumber(J, J->base[0]);
|
||||
uint32_t op = rd->data;
|
||||
BCReg i;
|
||||
for (i = 1; J->base[i] != 0; i++) {
|
||||
TRef tr2 = lj_ir_tonumber(J, J->base[i]);
|
||||
IRType t = IRT_INT;
|
||||
if (!(tref_isinteger(tr) && tref_isinteger(tr2))) {
|
||||
if (tref_isinteger(tr)) tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT);
|
||||
if (tref_isinteger(tr2)) tr2 = emitir(IRTN(IR_CONV), tr2, IRCONV_NUM_INT);
|
||||
t = IRT_NUM;
|
||||
}
|
||||
tr = emitir(IRT(op, t), tr, tr2);
|
||||
}
|
||||
J->base[0] = tr;
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_math_random(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
GCudata *ud = udataV(&J->fn->c.upvalue[0]);
|
||||
TRef tr, one;
|
||||
lj_ir_kgc(J, obj2gco(ud), IRT_UDATA); /* Prevent collection. */
|
||||
tr = lj_ir_call(J, IRCALL_lj_math_random_step, lj_ir_kptr(J, uddata(ud)));
|
||||
one = lj_ir_knum_one(J);
|
||||
tr = emitir(IRTN(IR_SUB), tr, one);
|
||||
if (J->base[0]) {
|
||||
TRef tr1 = lj_ir_tonum(J, J->base[0]);
|
||||
if (J->base[1]) { /* d = floor(d*(r2-r1+1.0)) + r1 */
|
||||
TRef tr2 = lj_ir_tonum(J, J->base[1]);
|
||||
tr2 = emitir(IRTN(IR_SUB), tr2, tr1);
|
||||
tr2 = emitir(IRTN(IR_ADD), tr2, one);
|
||||
tr = emitir(IRTN(IR_MUL), tr, tr2);
|
||||
tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR);
|
||||
tr = emitir(IRTN(IR_ADD), tr, tr1);
|
||||
} else { /* d = floor(d*r1) + 1.0 */
|
||||
tr = emitir(IRTN(IR_MUL), tr, tr1);
|
||||
tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR);
|
||||
tr = emitir(IRTN(IR_ADD), tr, one);
|
||||
}
|
||||
}
|
||||
J->base[0] = tr;
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* -- Bit library fast functions ------------------------------------------ */
|
||||
|
||||
/* Record unary bit.tobit, bit.bnot, bit.bswap. */
|
||||
static void LJ_FASTCALL recff_bit_unary(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_opt_narrow_tobit(J, J->base[0]);
|
||||
J->base[0] = (rd->data == IR_TOBIT) ? tr : emitir(IRTI(rd->data), tr, 0);
|
||||
}
|
||||
|
||||
/* Record N-ary bit.band, bit.bor, bit.bxor. */
|
||||
static void LJ_FASTCALL recff_bit_nary(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_opt_narrow_tobit(J, J->base[0]);
|
||||
uint32_t op = rd->data;
|
||||
BCReg i;
|
||||
for (i = 1; J->base[i] != 0; i++)
|
||||
tr = emitir(IRTI(op), tr, lj_opt_narrow_tobit(J, J->base[i]));
|
||||
J->base[0] = tr;
|
||||
}
|
||||
|
||||
/* Record bit shifts. */
|
||||
static void LJ_FASTCALL recff_bit_shift(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tr = lj_opt_narrow_tobit(J, J->base[0]);
|
||||
TRef tsh = lj_opt_narrow_tobit(J, J->base[1]);
|
||||
IROp op = (IROp)rd->data;
|
||||
if (!(op < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) &&
|
||||
!tref_isk(tsh))
|
||||
tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 31));
|
||||
#ifdef LJ_TARGET_UNIFYROT
|
||||
if (op == (LJ_TARGET_UNIFYROT == 1 ? IR_BROR : IR_BROL)) {
|
||||
op = LJ_TARGET_UNIFYROT == 1 ? IR_BROL : IR_BROR;
|
||||
tsh = emitir(IRTI(IR_NEG), tsh, tsh);
|
||||
}
|
||||
#endif
|
||||
J->base[0] = emitir(IRTI(op), tr, tsh);
|
||||
}
|
||||
|
||||
/* -- String library fast functions --------------------------------------- */
|
||||
|
||||
static void LJ_FASTCALL recff_string_len(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
J->base[0] = emitir(IRTI(IR_FLOAD), lj_ir_tostr(J, J->base[0]), IRFL_STR_LEN);
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
/* Handle string.byte (rd->data = 0) and string.sub (rd->data = 1). */
|
||||
static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef trstr = lj_ir_tostr(J, J->base[0]);
|
||||
TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN);
|
||||
TRef tr0 = lj_ir_kint(J, 0);
|
||||
TRef trstart, trend;
|
||||
GCstr *str = argv2str(J, &rd->argv[0]);
|
||||
int32_t start, end;
|
||||
if (rd->data) { /* string.sub(str, start [,end]) */
|
||||
start = argv2int(J, &rd->argv[1]);
|
||||
trstart = lj_opt_narrow_toint(J, J->base[1]);
|
||||
trend = J->base[2];
|
||||
if (tref_isnil(trend)) {
|
||||
trend = lj_ir_kint(J, -1);
|
||||
end = -1;
|
||||
} else {
|
||||
trend = lj_opt_narrow_toint(J, trend);
|
||||
end = argv2int(J, &rd->argv[2]);
|
||||
}
|
||||
} else { /* string.byte(str, [,start [,end]]) */
|
||||
if (J->base[1]) {
|
||||
start = argv2int(J, &rd->argv[1]);
|
||||
trstart = lj_opt_narrow_toint(J, J->base[1]);
|
||||
trend = J->base[2];
|
||||
if (tref_isnil(trend)) {
|
||||
trend = trstart;
|
||||
end = start;
|
||||
} else {
|
||||
trend = lj_opt_narrow_toint(J, trend);
|
||||
end = argv2int(J, &rd->argv[2]);
|
||||
}
|
||||
} else {
|
||||
trend = trstart = lj_ir_kint(J, 1);
|
||||
end = start = 1;
|
||||
}
|
||||
}
|
||||
if (end < 0) {
|
||||
emitir(IRTGI(IR_LT), trend, tr0);
|
||||
trend = emitir(IRTI(IR_ADD), emitir(IRTI(IR_ADD), trlen, trend),
|
||||
lj_ir_kint(J, 1));
|
||||
end = end+(int32_t)str->len+1;
|
||||
} else if ((MSize)end <= str->len) {
|
||||
emitir(IRTGI(IR_ULE), trend, trlen);
|
||||
} else {
|
||||
emitir(IRTGI(IR_GT), trend, trlen);
|
||||
end = (int32_t)str->len;
|
||||
trend = trlen;
|
||||
}
|
||||
if (start < 0) {
|
||||
emitir(IRTGI(IR_LT), trstart, tr0);
|
||||
trstart = emitir(IRTI(IR_ADD), trlen, trstart);
|
||||
start = start+(int32_t)str->len;
|
||||
emitir(start < 0 ? IRTGI(IR_LT) : IRTGI(IR_GE), trstart, tr0);
|
||||
if (start < 0) {
|
||||
trstart = tr0;
|
||||
start = 0;
|
||||
}
|
||||
} else {
|
||||
if (start == 0) {
|
||||
emitir(IRTGI(IR_EQ), trstart, tr0);
|
||||
trstart = tr0;
|
||||
} else {
|
||||
trstart = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, -1));
|
||||
emitir(IRTGI(IR_GE), trstart, tr0);
|
||||
start--;
|
||||
}
|
||||
}
|
||||
if (rd->data) { /* Return string.sub result. */
|
||||
if (end - start >= 0) {
|
||||
/* Also handle empty range here, to avoid extra traces. */
|
||||
TRef trptr, trslen = emitir(IRTI(IR_SUB), trend, trstart);
|
||||
emitir(IRTGI(IR_GE), trslen, tr0);
|
||||
trptr = emitir(IRT(IR_STRREF, IRT_P32), trstr, trstart);
|
||||
J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, trslen);
|
||||
} else { /* Range underflow: return empty string. */
|
||||
emitir(IRTGI(IR_LT), trend, trstart);
|
||||
J->base[0] = lj_ir_kstr(J, lj_str_new(J->L, strdata(str), 0));
|
||||
}
|
||||
} else { /* Return string.byte result(s). */
|
||||
ptrdiff_t i, len = end - start;
|
||||
if (len > 0) {
|
||||
TRef trslen = emitir(IRTI(IR_SUB), trend, trstart);
|
||||
emitir(IRTGI(IR_EQ), trslen, lj_ir_kint(J, (int32_t)len));
|
||||
if (J->baseslot + len > LJ_MAX_JSLOTS)
|
||||
lj_trace_err_info(J, LJ_TRERR_STACKOV);
|
||||
rd->nres = len;
|
||||
for (i = 0; i < len; i++) {
|
||||
TRef tmp = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, (int32_t)i));
|
||||
tmp = emitir(IRT(IR_STRREF, IRT_P32), trstr, tmp);
|
||||
J->base[i] = emitir(IRT(IR_XLOAD, IRT_U8), tmp, IRXLOAD_READONLY);
|
||||
}
|
||||
} else { /* Empty range or range underflow: return no results. */
|
||||
emitir(IRTGI(IR_LE), trend, trstart);
|
||||
rd->nres = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Table library fast functions ---------------------------------------- */
|
||||
|
||||
static void LJ_FASTCALL recff_table_getn(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
if (tref_istab(J->base[0]))
|
||||
J->base[0] = lj_ir_call(J, IRCALL_lj_tab_len, J->base[0]);
|
||||
/* else: Interpreter will throw. */
|
||||
UNUSED(rd);
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_table_remove(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef tab = J->base[0];
|
||||
rd->nres = 0;
|
||||
if (tref_istab(tab)) {
|
||||
if (!J->base[1] || tref_isnil(J->base[1])) { /* Simple pop: t[#t] = nil */
|
||||
TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, tab);
|
||||
GCtab *t = tabV(&rd->argv[0]);
|
||||
MSize len = lj_tab_len(t);
|
||||
emitir(IRTGI(len ? IR_NE : IR_EQ), trlen, lj_ir_kint(J, 0));
|
||||
if (len) {
|
||||
RecordIndex ix;
|
||||
ix.tab = tab;
|
||||
ix.key = trlen;
|
||||
settabV(J->L, &ix.tabv, t);
|
||||
setintV(&ix.keyv, len);
|
||||
ix.idxchain = 0;
|
||||
if (results_wanted(J) != 0) { /* Specialize load only if needed. */
|
||||
ix.val = 0;
|
||||
J->base[0] = lj_record_idx(J, &ix); /* Load previous value. */
|
||||
rd->nres = 1;
|
||||
/* Assumes ix.key/ix.tab is not modified for raw lj_record_idx(). */
|
||||
}
|
||||
ix.val = TREF_NIL;
|
||||
lj_record_idx(J, &ix); /* Remove value. */
|
||||
}
|
||||
} else { /* Complex case: remove in the middle. */
|
||||
recff_nyiu(J);
|
||||
}
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_table_insert(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
RecordIndex ix;
|
||||
ix.tab = J->base[0];
|
||||
ix.val = J->base[1];
|
||||
rd->nres = 0;
|
||||
if (tref_istab(ix.tab) && ix.val) {
|
||||
if (!J->base[2]) { /* Simple push: t[#t+1] = v */
|
||||
TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, ix.tab);
|
||||
GCtab *t = tabV(&rd->argv[0]);
|
||||
ix.key = emitir(IRTI(IR_ADD), trlen, lj_ir_kint(J, 1));
|
||||
settabV(J->L, &ix.tabv, t);
|
||||
setintV(&ix.keyv, lj_tab_len(t) + 1);
|
||||
ix.idxchain = 0;
|
||||
lj_record_idx(J, &ix); /* Set new value. */
|
||||
} else { /* Complex case: insert in the middle. */
|
||||
recff_nyiu(J);
|
||||
}
|
||||
} /* else: Interpreter will throw. */
|
||||
}
|
||||
|
||||
/* -- I/O library fast functions ------------------------------------------ */
|
||||
|
||||
/* Get FILE* for I/O function. Any I/O error aborts recording, so there's
|
||||
** no need to encode the alternate cases for any of the guards.
|
||||
*/
|
||||
static TRef recff_io_fp(jit_State *J, TRef *udp, int32_t id)
|
||||
{
|
||||
TRef tr, ud, fp;
|
||||
if (id) { /* io.func() */
|
||||
tr = lj_ir_kptr(J, &J2G(J)->gcroot[id]);
|
||||
ud = emitir(IRT(IR_XLOAD, IRT_UDATA), tr, 0);
|
||||
} else { /* fp:method() */
|
||||
ud = J->base[0];
|
||||
if (!tref_isudata(ud))
|
||||
lj_trace_err(J, LJ_TRERR_BADTYPE);
|
||||
tr = emitir(IRT(IR_FLOAD, IRT_U8), ud, IRFL_UDATA_UDTYPE);
|
||||
emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE));
|
||||
}
|
||||
*udp = ud;
|
||||
fp = emitir(IRT(IR_FLOAD, IRT_PTR), ud, IRFL_UDATA_FILE);
|
||||
emitir(IRTG(IR_NE, IRT_PTR), fp, lj_ir_knull(J, IRT_PTR));
|
||||
return fp;
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_io_write(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef ud, fp = recff_io_fp(J, &ud, rd->data);
|
||||
TRef zero = lj_ir_kint(J, 0);
|
||||
TRef one = lj_ir_kint(J, 1);
|
||||
ptrdiff_t i = rd->data == 0 ? 1 : 0;
|
||||
for (; J->base[i]; i++) {
|
||||
TRef str = lj_ir_tostr(J, J->base[i]);
|
||||
TRef buf = emitir(IRT(IR_STRREF, IRT_P32), str, zero);
|
||||
TRef len = emitir(IRTI(IR_FLOAD), str, IRFL_STR_LEN);
|
||||
if (tref_isk(len) && IR(tref_ref(len))->i == 1) {
|
||||
TRef tr = emitir(IRT(IR_XLOAD, IRT_U8), buf, IRXLOAD_READONLY);
|
||||
tr = lj_ir_call(J, IRCALL_fputc, tr, fp);
|
||||
if (results_wanted(J) != 0) /* Check result only if not ignored. */
|
||||
emitir(IRTGI(IR_NE), tr, lj_ir_kint(J, -1));
|
||||
} else {
|
||||
TRef tr = lj_ir_call(J, IRCALL_fwrite, buf, one, len, fp);
|
||||
if (results_wanted(J) != 0) /* Check result only if not ignored. */
|
||||
emitir(IRTGI(IR_EQ), tr, len);
|
||||
}
|
||||
}
|
||||
J->base[0] = LJ_52 ? ud : TREF_TRUE;
|
||||
}
|
||||
|
||||
static void LJ_FASTCALL recff_io_flush(jit_State *J, RecordFFData *rd)
|
||||
{
|
||||
TRef ud, fp = recff_io_fp(J, &ud, rd->data);
|
||||
TRef tr = lj_ir_call(J, IRCALL_fflush, fp);
|
||||
if (results_wanted(J) != 0) /* Check result only if not ignored. */
|
||||
emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, 0));
|
||||
J->base[0] = TREF_TRUE;
|
||||
}
|
||||
|
||||
/* -- Record calls to fast functions -------------------------------------- */
|
||||
|
||||
#include "lj_recdef.h"
|
||||
|
||||
static uint32_t recdef_lookup(GCfunc *fn)
|
||||
{
|
||||
if (fn->c.ffid < sizeof(recff_idmap)/sizeof(recff_idmap[0]))
|
||||
return recff_idmap[fn->c.ffid];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Record entry to a fast function or C function. */
|
||||
void lj_ffrecord_func(jit_State *J)
|
||||
{
|
||||
RecordFFData rd;
|
||||
uint32_t m = recdef_lookup(J->fn);
|
||||
rd.data = m & 0xff;
|
||||
rd.nres = 1; /* Default is one result. */
|
||||
rd.argv = J->L->base;
|
||||
J->base[J->maxslot] = 0; /* Mark end of arguments. */
|
||||
(recff_func[m >> 8])(J, &rd); /* Call recff_* handler. */
|
||||
if (rd.nres >= 0) {
|
||||
if (J->postproc == LJ_POST_NONE) J->postproc = LJ_POST_FFRETRY;
|
||||
lj_record_ret(J, 0, rd.nres);
|
||||
}
|
||||
}
|
||||
|
||||
#undef IR
|
||||
#undef emitir
|
||||
|
||||
#endif
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
** Stack frames.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_FRAME_H
|
||||
#define _LJ_FRAME_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
/* -- Lua stack frame ----------------------------------------------------- */
|
||||
|
||||
/* Frame type markers in callee function slot (callee base-1). */
|
||||
enum {
|
||||
FRAME_LUA, FRAME_C, FRAME_CONT, FRAME_VARG,
|
||||
FRAME_LUAP, FRAME_CP, FRAME_PCALL, FRAME_PCALLH
|
||||
};
|
||||
#define FRAME_TYPE 3
|
||||
#define FRAME_P 4
|
||||
#define FRAME_TYPEP (FRAME_TYPE|FRAME_P)
|
||||
|
||||
/* Macros to access and modify Lua frames. */
|
||||
#define frame_gc(f) (gcref((f)->fr.func))
|
||||
#define frame_func(f) (&frame_gc(f)->fn)
|
||||
#define frame_ftsz(f) ((f)->fr.tp.ftsz)
|
||||
|
||||
#define frame_type(f) (frame_ftsz(f) & FRAME_TYPE)
|
||||
#define frame_typep(f) (frame_ftsz(f) & FRAME_TYPEP)
|
||||
#define frame_islua(f) (frame_type(f) == FRAME_LUA)
|
||||
#define frame_isc(f) (frame_type(f) == FRAME_C)
|
||||
#define frame_iscont(f) (frame_typep(f) == FRAME_CONT)
|
||||
#define frame_isvarg(f) (frame_typep(f) == FRAME_VARG)
|
||||
#define frame_ispcall(f) ((frame_ftsz(f) & 6) == FRAME_PCALL)
|
||||
|
||||
#define frame_pc(f) (mref((f)->fr.tp.pcr, const BCIns))
|
||||
#define frame_contpc(f) (frame_pc((f)-1))
|
||||
#if LJ_64
|
||||
#define frame_contf(f) \
|
||||
((ASMFunction)(void *)((intptr_t)lj_vm_asm_begin + \
|
||||
(intptr_t)(int32_t)((f)-1)->u32.lo))
|
||||
#else
|
||||
#define frame_contf(f) ((ASMFunction)gcrefp(((f)-1)->gcr, void))
|
||||
#endif
|
||||
#define frame_delta(f) (frame_ftsz(f) >> 3)
|
||||
#define frame_sized(f) (frame_ftsz(f) & ~FRAME_TYPEP)
|
||||
|
||||
#define frame_prevl(f) ((f) - (1+bc_a(frame_pc(f)[-1])))
|
||||
#define frame_prevd(f) ((TValue *)((char *)(f) - frame_sized(f)))
|
||||
#define frame_prev(f) (frame_islua(f)?frame_prevl(f):frame_prevd(f))
|
||||
/* Note: this macro does not skip over FRAME_VARG. */
|
||||
|
||||
#define setframe_pc(f, pc) (setmref((f)->fr.tp.pcr, (pc)))
|
||||
#define setframe_ftsz(f, sz) ((f)->fr.tp.ftsz = (sz))
|
||||
#define setframe_gc(f, p) (setgcref((f)->fr.func, (p)))
|
||||
|
||||
/* -- C stack frame ------------------------------------------------------- */
|
||||
|
||||
/* Macros to access and modify the C stack frame chain. */
|
||||
|
||||
/* These definitions must match with the arch-specific *.dasc files. */
|
||||
#if LJ_TARGET_X86
|
||||
#define CFRAME_OFS_ERRF (15*4)
|
||||
#define CFRAME_OFS_NRES (14*4)
|
||||
#define CFRAME_OFS_PREV (13*4)
|
||||
#define CFRAME_OFS_L (12*4)
|
||||
#define CFRAME_OFS_PC (6*4)
|
||||
#define CFRAME_OFS_MULTRES (5*4)
|
||||
#define CFRAME_SIZE (12*4)
|
||||
#define CFRAME_SHIFT_MULTRES 0
|
||||
#elif LJ_TARGET_X64
|
||||
#if LJ_ABI_WIN
|
||||
#define CFRAME_OFS_PREV (13*8)
|
||||
#define CFRAME_OFS_PC (25*4)
|
||||
#define CFRAME_OFS_L (24*4)
|
||||
#define CFRAME_OFS_ERRF (23*4)
|
||||
#define CFRAME_OFS_NRES (22*4)
|
||||
#define CFRAME_OFS_MULTRES (21*4)
|
||||
#define CFRAME_SIZE (10*8)
|
||||
#define CFRAME_SIZE_JIT (CFRAME_SIZE + 9*16 + 4*8)
|
||||
#define CFRAME_SHIFT_MULTRES 0
|
||||
#else
|
||||
#define CFRAME_OFS_PREV (4*8)
|
||||
#define CFRAME_OFS_PC (7*4)
|
||||
#define CFRAME_OFS_L (6*4)
|
||||
#define CFRAME_OFS_ERRF (5*4)
|
||||
#define CFRAME_OFS_NRES (4*4)
|
||||
#define CFRAME_OFS_MULTRES (1*4)
|
||||
#define CFRAME_SIZE (10*8)
|
||||
#define CFRAME_SIZE_JIT (CFRAME_SIZE + 16)
|
||||
#define CFRAME_SHIFT_MULTRES 0
|
||||
#endif
|
||||
#elif LJ_TARGET_ARM
|
||||
#define CFRAME_OFS_ERRF 24
|
||||
#define CFRAME_OFS_NRES 20
|
||||
#define CFRAME_OFS_PREV 16
|
||||
#define CFRAME_OFS_L 12
|
||||
#define CFRAME_OFS_PC 8
|
||||
#define CFRAME_OFS_MULTRES 4
|
||||
#if LJ_ARCH_HASFPU
|
||||
#define CFRAME_SIZE 128
|
||||
#else
|
||||
#define CFRAME_SIZE 64
|
||||
#endif
|
||||
#define CFRAME_SHIFT_MULTRES 3
|
||||
#elif LJ_TARGET_PPC
|
||||
#if LJ_TARGET_XBOX360
|
||||
#define CFRAME_OFS_ERRF 424
|
||||
#define CFRAME_OFS_NRES 420
|
||||
#define CFRAME_OFS_PREV 400
|
||||
#define CFRAME_OFS_L 416
|
||||
#define CFRAME_OFS_PC 412
|
||||
#define CFRAME_OFS_MULTRES 408
|
||||
#define CFRAME_SIZE 384
|
||||
#define CFRAME_SHIFT_MULTRES 3
|
||||
#elif LJ_ARCH_PPC64
|
||||
#define CFRAME_OFS_ERRF 472
|
||||
#define CFRAME_OFS_NRES 468
|
||||
#define CFRAME_OFS_PREV 448
|
||||
#define CFRAME_OFS_L 464
|
||||
#define CFRAME_OFS_PC 460
|
||||
#define CFRAME_OFS_MULTRES 456
|
||||
#define CFRAME_SIZE 400
|
||||
#define CFRAME_SHIFT_MULTRES 3
|
||||
#else
|
||||
#define CFRAME_OFS_ERRF 48
|
||||
#define CFRAME_OFS_NRES 44
|
||||
#define CFRAME_OFS_PREV 40
|
||||
#define CFRAME_OFS_L 36
|
||||
#define CFRAME_OFS_PC 32
|
||||
#define CFRAME_OFS_MULTRES 28
|
||||
#define CFRAME_SIZE 272
|
||||
#define CFRAME_SHIFT_MULTRES 3
|
||||
#endif
|
||||
#elif LJ_TARGET_PPCSPE
|
||||
#define CFRAME_OFS_ERRF 28
|
||||
#define CFRAME_OFS_NRES 24
|
||||
#define CFRAME_OFS_PREV 20
|
||||
#define CFRAME_OFS_L 16
|
||||
#define CFRAME_OFS_PC 12
|
||||
#define CFRAME_OFS_MULTRES 8
|
||||
#define CFRAME_SIZE 184
|
||||
#define CFRAME_SHIFT_MULTRES 3
|
||||
#elif LJ_TARGET_MIPS
|
||||
#define CFRAME_OFS_ERRF 124
|
||||
#define CFRAME_OFS_NRES 120
|
||||
#define CFRAME_OFS_PREV 116
|
||||
#define CFRAME_OFS_L 112
|
||||
#define CFRAME_OFS_PC 20
|
||||
#define CFRAME_OFS_MULTRES 16
|
||||
#define CFRAME_SIZE 112
|
||||
#define CFRAME_SHIFT_MULTRES 3
|
||||
#else
|
||||
#error "Missing CFRAME_* definitions for this architecture"
|
||||
#endif
|
||||
|
||||
#ifndef CFRAME_SIZE_JIT
|
||||
#define CFRAME_SIZE_JIT CFRAME_SIZE
|
||||
#endif
|
||||
|
||||
#define CFRAME_RESUME 1
|
||||
#define CFRAME_UNWIND_FF 2 /* Only used in unwinder. */
|
||||
#define CFRAME_RAWMASK (~(intptr_t)(CFRAME_RESUME|CFRAME_UNWIND_FF))
|
||||
|
||||
#define cframe_errfunc(cf) (*(int32_t *)(((char *)(cf))+CFRAME_OFS_ERRF))
|
||||
#define cframe_nres(cf) (*(int32_t *)(((char *)(cf))+CFRAME_OFS_NRES))
|
||||
#define cframe_prev(cf) (*(void **)(((char *)(cf))+CFRAME_OFS_PREV))
|
||||
#define cframe_multres(cf) (*(uint32_t *)(((char *)(cf))+CFRAME_OFS_MULTRES))
|
||||
#define cframe_multres_n(cf) (cframe_multres((cf)) >> CFRAME_SHIFT_MULTRES)
|
||||
#define cframe_L(cf) \
|
||||
(&gcref(*(GCRef *)(((char *)(cf))+CFRAME_OFS_L))->th)
|
||||
#define cframe_pc(cf) \
|
||||
(mref(*(MRef *)(((char *)(cf))+CFRAME_OFS_PC), const BCIns))
|
||||
#define setcframe_L(cf, L) \
|
||||
(setmref(*(MRef *)(((char *)(cf))+CFRAME_OFS_L), (L)))
|
||||
#define setcframe_pc(cf, pc) \
|
||||
(setmref(*(MRef *)(((char *)(cf))+CFRAME_OFS_PC), (pc)))
|
||||
#define cframe_canyield(cf) ((intptr_t)(cf) & CFRAME_RESUME)
|
||||
#define cframe_unwind_ff(cf) ((intptr_t)(cf) & CFRAME_UNWIND_FF)
|
||||
#define cframe_raw(cf) ((void *)((intptr_t)(cf) & CFRAME_RAWMASK))
|
||||
#define cframe_Lpc(L) cframe_pc(cframe_raw(L->cframe))
|
||||
|
||||
#endif
|
||||
@@ -1,271 +0,0 @@
|
||||
/*
|
||||
** IR CALL* instruction definitions.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_IRCALL_H
|
||||
#define _LJ_IRCALL_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_ir.h"
|
||||
#include "lj_jit.h"
|
||||
|
||||
/* C call info for CALL* instructions. */
|
||||
typedef struct CCallInfo {
|
||||
ASMFunction func; /* Function pointer. */
|
||||
uint32_t flags; /* Number of arguments and flags. */
|
||||
} CCallInfo;
|
||||
|
||||
#define CCI_NARGS(ci) ((ci)->flags & 0xff) /* Extract # of args. */
|
||||
#define CCI_NARGS_MAX 32 /* Max. # of args. */
|
||||
|
||||
#define CCI_OTSHIFT 16
|
||||
#define CCI_OPTYPE(ci) ((ci)->flags >> CCI_OTSHIFT) /* Get op/type. */
|
||||
#define CCI_OPSHIFT 24
|
||||
#define CCI_OP(ci) ((ci)->flags >> CCI_OPSHIFT) /* Get op. */
|
||||
|
||||
#define CCI_CALL_N (IR_CALLN << CCI_OPSHIFT)
|
||||
#define CCI_CALL_L (IR_CALLL << CCI_OPSHIFT)
|
||||
#define CCI_CALL_S (IR_CALLS << CCI_OPSHIFT)
|
||||
#define CCI_CALL_FN (CCI_CALL_N|CCI_CC_FASTCALL)
|
||||
#define CCI_CALL_FL (CCI_CALL_L|CCI_CC_FASTCALL)
|
||||
#define CCI_CALL_FS (CCI_CALL_S|CCI_CC_FASTCALL)
|
||||
|
||||
/* C call info flags. */
|
||||
#define CCI_L 0x0100 /* Implicit L arg. */
|
||||
#define CCI_CASTU64 0x0200 /* Cast u64 result to number. */
|
||||
#define CCI_NOFPRCLOBBER 0x0400 /* Does not clobber any FPRs. */
|
||||
#define CCI_VARARG 0x0800 /* Vararg function. */
|
||||
|
||||
#define CCI_CC_MASK 0x3000 /* Calling convention mask. */
|
||||
#define CCI_CC_SHIFT 12
|
||||
/* ORDER CC */
|
||||
#define CCI_CC_CDECL 0x0000 /* Default cdecl calling convention. */
|
||||
#define CCI_CC_THISCALL 0x1000 /* Thiscall calling convention. */
|
||||
#define CCI_CC_FASTCALL 0x2000 /* Fastcall calling convention. */
|
||||
#define CCI_CC_STDCALL 0x3000 /* Stdcall calling convention. */
|
||||
|
||||
/* Helpers for conditional function definitions. */
|
||||
#define IRCALLCOND_ANY(x) x
|
||||
|
||||
#if LJ_TARGET_X86ORX64
|
||||
#define IRCALLCOND_FPMATH(x) NULL
|
||||
#else
|
||||
#define IRCALLCOND_FPMATH(x) x
|
||||
#endif
|
||||
|
||||
#if LJ_SOFTFP
|
||||
#define IRCALLCOND_SOFTFP(x) x
|
||||
#if LJ_HASFFI
|
||||
#define IRCALLCOND_SOFTFP_FFI(x) x
|
||||
#else
|
||||
#define IRCALLCOND_SOFTFP_FFI(x) NULL
|
||||
#endif
|
||||
#else
|
||||
#define IRCALLCOND_SOFTFP(x) NULL
|
||||
#define IRCALLCOND_SOFTFP_FFI(x) NULL
|
||||
#endif
|
||||
|
||||
#define LJ_NEED_FP64 (LJ_TARGET_ARM || LJ_TARGET_PPC || LJ_TARGET_MIPS)
|
||||
|
||||
#if LJ_HASFFI && (LJ_SOFTFP || LJ_NEED_FP64)
|
||||
#define IRCALLCOND_FP64_FFI(x) x
|
||||
#else
|
||||
#define IRCALLCOND_FP64_FFI(x) NULL
|
||||
#endif
|
||||
|
||||
#if LJ_HASFFI
|
||||
#define IRCALLCOND_FFI(x) x
|
||||
#if LJ_32
|
||||
#define IRCALLCOND_FFI32(x) x
|
||||
#else
|
||||
#define IRCALLCOND_FFI32(x) NULL
|
||||
#endif
|
||||
#else
|
||||
#define IRCALLCOND_FFI(x) NULL
|
||||
#define IRCALLCOND_FFI32(x) NULL
|
||||
#endif
|
||||
|
||||
#if LJ_SOFTFP
|
||||
#define ARG1_FP 2 /* Treat as 2 32 bit arguments. */
|
||||
#else
|
||||
#define ARG1_FP 1
|
||||
#endif
|
||||
|
||||
#if LJ_32
|
||||
#define ARG2_64 4 /* Treat as 4 32 bit arguments. */
|
||||
#else
|
||||
#define ARG2_64 2
|
||||
#endif
|
||||
|
||||
/* Function definitions for CALL* instructions. */
|
||||
#define IRCALLDEF(_) \
|
||||
_(ANY, lj_str_cmp, 2, FN, INT, CCI_NOFPRCLOBBER) \
|
||||
_(ANY, lj_str_new, 3, S, STR, CCI_L) \
|
||||
_(ANY, lj_strscan_num, 2, FN, INT, 0) \
|
||||
_(ANY, lj_str_fromint, 2, FN, STR, CCI_L) \
|
||||
_(ANY, lj_str_fromnum, 2, FN, STR, CCI_L) \
|
||||
_(ANY, lj_tab_new1, 2, FS, TAB, CCI_L) \
|
||||
_(ANY, lj_tab_dup, 2, FS, TAB, CCI_L) \
|
||||
_(ANY, lj_tab_newkey, 3, S, P32, CCI_L) \
|
||||
_(ANY, lj_tab_len, 1, FL, INT, 0) \
|
||||
_(ANY, lj_gc_step_jit, 2, FS, NIL, CCI_L) \
|
||||
_(ANY, lj_gc_barrieruv, 2, FS, NIL, 0) \
|
||||
_(ANY, lj_mem_newgco, 2, FS, P32, CCI_L) \
|
||||
_(ANY, lj_math_random_step, 1, FS, NUM, CCI_CASTU64|CCI_NOFPRCLOBBER) \
|
||||
_(ANY, lj_vm_modi, 2, FN, INT, 0) \
|
||||
_(ANY, sinh, ARG1_FP, N, NUM, 0) \
|
||||
_(ANY, cosh, ARG1_FP, N, NUM, 0) \
|
||||
_(ANY, tanh, ARG1_FP, N, NUM, 0) \
|
||||
_(ANY, fputc, 2, S, INT, 0) \
|
||||
_(ANY, fwrite, 4, S, INT, 0) \
|
||||
_(ANY, fflush, 1, S, INT, 0) \
|
||||
/* ORDER FPM */ \
|
||||
_(FPMATH, lj_vm_floor, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, lj_vm_ceil, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, lj_vm_trunc, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, sqrt, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, exp, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, lj_vm_exp2, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, log, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, lj_vm_log2, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, log10, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, sin, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, cos, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, tan, ARG1_FP, N, NUM, 0) \
|
||||
_(FPMATH, lj_vm_powi, ARG1_FP+1, N, NUM, 0) \
|
||||
_(FPMATH, pow, ARG1_FP*2, N, NUM, 0) \
|
||||
_(FPMATH, atan2, ARG1_FP*2, N, NUM, 0) \
|
||||
_(FPMATH, ldexp, ARG1_FP+1, N, NUM, 0) \
|
||||
_(SOFTFP, lj_vm_tobit, 2, N, INT, 0) \
|
||||
_(SOFTFP, softfp_add, 4, N, NUM, 0) \
|
||||
_(SOFTFP, softfp_sub, 4, N, NUM, 0) \
|
||||
_(SOFTFP, softfp_mul, 4, N, NUM, 0) \
|
||||
_(SOFTFP, softfp_div, 4, N, NUM, 0) \
|
||||
_(SOFTFP, softfp_cmp, 4, N, NIL, 0) \
|
||||
_(SOFTFP, softfp_i2d, 1, N, NUM, 0) \
|
||||
_(SOFTFP, softfp_d2i, 2, N, INT, 0) \
|
||||
_(SOFTFP_FFI, softfp_ui2d, 1, N, NUM, 0) \
|
||||
_(SOFTFP_FFI, softfp_f2d, 1, N, NUM, 0) \
|
||||
_(SOFTFP_FFI, softfp_d2ui, 2, N, INT, 0) \
|
||||
_(SOFTFP_FFI, softfp_d2f, 2, N, FLOAT, 0) \
|
||||
_(SOFTFP_FFI, softfp_i2f, 1, N, FLOAT, 0) \
|
||||
_(SOFTFP_FFI, softfp_ui2f, 1, N, FLOAT, 0) \
|
||||
_(SOFTFP_FFI, softfp_f2i, 1, N, INT, 0) \
|
||||
_(SOFTFP_FFI, softfp_f2ui, 1, N, INT, 0) \
|
||||
_(FP64_FFI, fp64_l2d, 2, N, NUM, 0) \
|
||||
_(FP64_FFI, fp64_ul2d, 2, N, NUM, 0) \
|
||||
_(FP64_FFI, fp64_l2f, 2, N, FLOAT, 0) \
|
||||
_(FP64_FFI, fp64_ul2f, 2, N, FLOAT, 0) \
|
||||
_(FP64_FFI, fp64_d2l, ARG1_FP, N, I64, 0) \
|
||||
_(FP64_FFI, fp64_d2ul, ARG1_FP, N, U64, 0) \
|
||||
_(FP64_FFI, fp64_f2l, 1, N, I64, 0) \
|
||||
_(FP64_FFI, fp64_f2ul, 1, N, U64, 0) \
|
||||
_(FFI, lj_carith_divi64, ARG2_64, N, I64, CCI_NOFPRCLOBBER) \
|
||||
_(FFI, lj_carith_divu64, ARG2_64, N, U64, CCI_NOFPRCLOBBER) \
|
||||
_(FFI, lj_carith_modi64, ARG2_64, N, I64, CCI_NOFPRCLOBBER) \
|
||||
_(FFI, lj_carith_modu64, ARG2_64, N, U64, CCI_NOFPRCLOBBER) \
|
||||
_(FFI, lj_carith_powi64, ARG2_64, N, I64, CCI_NOFPRCLOBBER) \
|
||||
_(FFI, lj_carith_powu64, ARG2_64, N, U64, CCI_NOFPRCLOBBER) \
|
||||
_(FFI, lj_cdata_setfin, 2, FN, P32, CCI_L) \
|
||||
_(FFI, strlen, 1, L, INTP, 0) \
|
||||
_(FFI, memcpy, 3, S, PTR, 0) \
|
||||
_(FFI, memset, 3, S, PTR, 0) \
|
||||
_(FFI, lj_vm_errno, 0, S, INT, CCI_NOFPRCLOBBER) \
|
||||
_(FFI32, lj_carith_mul64, ARG2_64, N, I64, CCI_NOFPRCLOBBER)
|
||||
\
|
||||
/* End of list. */
|
||||
|
||||
typedef enum {
|
||||
#define IRCALLENUM(cond, name, nargs, kind, type, flags) IRCALL_##name,
|
||||
IRCALLDEF(IRCALLENUM)
|
||||
#undef IRCALLENUM
|
||||
IRCALL__MAX
|
||||
} IRCallID;
|
||||
|
||||
LJ_FUNC TRef lj_ir_call(jit_State *J, IRCallID id, ...);
|
||||
|
||||
LJ_DATA const CCallInfo lj_ir_callinfo[IRCALL__MAX+1];
|
||||
|
||||
/* Soft-float declarations. */
|
||||
#if LJ_SOFTFP
|
||||
#if LJ_TARGET_ARM
|
||||
#define softfp_add __aeabi_dadd
|
||||
#define softfp_sub __aeabi_dsub
|
||||
#define softfp_mul __aeabi_dmul
|
||||
#define softfp_div __aeabi_ddiv
|
||||
#define softfp_cmp __aeabi_cdcmple
|
||||
#define softfp_i2d __aeabi_i2d
|
||||
#define softfp_d2i __aeabi_d2iz
|
||||
#define softfp_ui2d __aeabi_ui2d
|
||||
#define softfp_f2d __aeabi_f2d
|
||||
#define softfp_d2ui __aeabi_d2uiz
|
||||
#define softfp_d2f __aeabi_d2f
|
||||
#define softfp_i2f __aeabi_i2f
|
||||
#define softfp_ui2f __aeabi_ui2f
|
||||
#define softfp_f2i __aeabi_f2iz
|
||||
#define softfp_f2ui __aeabi_f2uiz
|
||||
#define fp64_l2d __aeabi_l2d
|
||||
#define fp64_ul2d __aeabi_ul2d
|
||||
#define fp64_l2f __aeabi_l2f
|
||||
#define fp64_ul2f __aeabi_ul2f
|
||||
#if LJ_TARGET_IOS
|
||||
#define fp64_d2l __fixdfdi
|
||||
#define fp64_d2ul __fixunsdfdi
|
||||
#define fp64_f2l __fixsfdi
|
||||
#define fp64_f2ul __fixunssfdi
|
||||
#else
|
||||
#define fp64_d2l __aeabi_d2lz
|
||||
#define fp64_d2ul __aeabi_d2ulz
|
||||
#define fp64_f2l __aeabi_f2lz
|
||||
#define fp64_f2ul __aeabi_f2ulz
|
||||
#endif
|
||||
#else
|
||||
#error "Missing soft-float definitions for target architecture"
|
||||
#endif
|
||||
extern double softfp_add(double a, double b);
|
||||
extern double softfp_sub(double a, double b);
|
||||
extern double softfp_mul(double a, double b);
|
||||
extern double softfp_div(double a, double b);
|
||||
extern void softfp_cmp(double a, double b);
|
||||
extern double softfp_i2d(int32_t a);
|
||||
extern int32_t softfp_d2i(double a);
|
||||
#if LJ_HASFFI
|
||||
extern double softfp_ui2d(uint32_t a);
|
||||
extern double softfp_f2d(float a);
|
||||
extern uint32_t softfp_d2ui(double a);
|
||||
extern float softfp_d2f(double a);
|
||||
extern float softfp_i2f(int32_t a);
|
||||
extern float softfp_ui2f(uint32_t a);
|
||||
extern int32_t softfp_f2i(float a);
|
||||
extern uint32_t softfp_f2ui(float a);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LJ_HASFFI && LJ_NEED_FP64 && !(LJ_TARGET_ARM && LJ_SOFTFP)
|
||||
#ifdef __GNUC__
|
||||
#define fp64_l2d __floatdidf
|
||||
#define fp64_ul2d __floatundidf
|
||||
#define fp64_l2f __floatdisf
|
||||
#define fp64_ul2f __floatundisf
|
||||
#define fp64_d2l __fixdfdi
|
||||
#define fp64_d2ul __fixunsdfdi
|
||||
#define fp64_f2l __fixsfdi
|
||||
#define fp64_f2ul __fixunssfdi
|
||||
#else
|
||||
#error "Missing fp64 helper definitions for this compiler"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LJ_HASFFI && (LJ_SOFTFP || LJ_NEED_FP64)
|
||||
extern double fp64_l2d(int64_t a);
|
||||
extern double fp64_ul2d(uint64_t a);
|
||||
extern float fp64_l2f(int64_t a);
|
||||
extern float fp64_ul2f(uint64_t a);
|
||||
extern int64_t fp64_d2l(double a);
|
||||
extern uint64_t fp64_d2ul(double a);
|
||||
extern int64_t fp64_f2l(float a);
|
||||
extern uint64_t fp64_f2ul(float a);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,416 +0,0 @@
|
||||
/*
|
||||
** Common definitions for the JIT compiler.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_JIT_H
|
||||
#define _LJ_JIT_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_ir.h"
|
||||
|
||||
/* JIT engine flags. */
|
||||
#define JIT_F_ON 0x00000001
|
||||
|
||||
/* CPU-specific JIT engine flags. */
|
||||
#if LJ_TARGET_X86ORX64
|
||||
#define JIT_F_CMOV 0x00000010
|
||||
#define JIT_F_SSE2 0x00000020
|
||||
#define JIT_F_SSE3 0x00000040
|
||||
#define JIT_F_SSE4_1 0x00000080
|
||||
#define JIT_F_P4 0x00000100
|
||||
#define JIT_F_PREFER_IMUL 0x00000200
|
||||
#define JIT_F_SPLIT_XMM 0x00000400
|
||||
#define JIT_F_LEA_AGU 0x00000800
|
||||
|
||||
/* Names for the CPU-specific flags. Must match the order above. */
|
||||
#define JIT_F_CPU_FIRST JIT_F_CMOV
|
||||
#define JIT_F_CPUSTRING "\4CMOV\4SSE2\4SSE3\6SSE4.1\2P4\3AMD\2K8\4ATOM"
|
||||
#elif LJ_TARGET_ARM
|
||||
#define JIT_F_ARMV6_ 0x00000010
|
||||
#define JIT_F_ARMV6T2_ 0x00000020
|
||||
#define JIT_F_ARMV7 0x00000040
|
||||
#define JIT_F_VFPV2 0x00000080
|
||||
#define JIT_F_VFPV3 0x00000100
|
||||
|
||||
#define JIT_F_ARMV6 (JIT_F_ARMV6_|JIT_F_ARMV6T2_|JIT_F_ARMV7)
|
||||
#define JIT_F_ARMV6T2 (JIT_F_ARMV6T2_|JIT_F_ARMV7)
|
||||
#define JIT_F_VFP (JIT_F_VFPV2|JIT_F_VFPV3)
|
||||
|
||||
/* Names for the CPU-specific flags. Must match the order above. */
|
||||
#define JIT_F_CPU_FIRST JIT_F_ARMV6_
|
||||
#define JIT_F_CPUSTRING "\5ARMv6\7ARMv6T2\5ARMv7\5VFPv2\5VFPv3"
|
||||
#elif LJ_TARGET_PPC
|
||||
#define JIT_F_SQRT 0x00000010
|
||||
#define JIT_F_ROUND 0x00000020
|
||||
|
||||
/* Names for the CPU-specific flags. Must match the order above. */
|
||||
#define JIT_F_CPU_FIRST JIT_F_SQRT
|
||||
#define JIT_F_CPUSTRING "\4SQRT\5ROUND"
|
||||
#elif LJ_TARGET_MIPS
|
||||
#define JIT_F_MIPS32R2 0x00000010
|
||||
|
||||
/* Names for the CPU-specific flags. Must match the order above. */
|
||||
#define JIT_F_CPU_FIRST JIT_F_MIPS32R2
|
||||
#define JIT_F_CPUSTRING "\010MIPS32R2"
|
||||
#else
|
||||
#define JIT_F_CPU_FIRST 0
|
||||
#define JIT_F_CPUSTRING ""
|
||||
#endif
|
||||
|
||||
/* Optimization flags. */
|
||||
#define JIT_F_OPT_MASK 0x0fff0000
|
||||
|
||||
#define JIT_F_OPT_FOLD 0x00010000
|
||||
#define JIT_F_OPT_CSE 0x00020000
|
||||
#define JIT_F_OPT_DCE 0x00040000
|
||||
#define JIT_F_OPT_FWD 0x00080000
|
||||
#define JIT_F_OPT_DSE 0x00100000
|
||||
#define JIT_F_OPT_NARROW 0x00200000
|
||||
#define JIT_F_OPT_LOOP 0x00400000
|
||||
#define JIT_F_OPT_ABC 0x00800000
|
||||
#define JIT_F_OPT_SINK 0x01000000
|
||||
#define JIT_F_OPT_FUSE 0x02000000
|
||||
|
||||
/* Optimizations names for -O. Must match the order above. */
|
||||
#define JIT_F_OPT_FIRST JIT_F_OPT_FOLD
|
||||
#define JIT_F_OPTSTRING \
|
||||
"\4fold\3cse\3dce\3fwd\3dse\6narrow\4loop\3abc\4sink\4fuse"
|
||||
|
||||
/* Optimization levels set a fixed combination of flags. */
|
||||
#define JIT_F_OPT_0 0
|
||||
#define JIT_F_OPT_1 (JIT_F_OPT_FOLD|JIT_F_OPT_CSE|JIT_F_OPT_DCE)
|
||||
#define JIT_F_OPT_2 (JIT_F_OPT_1|JIT_F_OPT_NARROW|JIT_F_OPT_LOOP)
|
||||
#define JIT_F_OPT_3 (JIT_F_OPT_2|\
|
||||
JIT_F_OPT_FWD|JIT_F_OPT_DSE|JIT_F_OPT_ABC|JIT_F_OPT_SINK|JIT_F_OPT_FUSE)
|
||||
#define JIT_F_OPT_DEFAULT JIT_F_OPT_3
|
||||
|
||||
#if LJ_TARGET_WINDOWS || LJ_64
|
||||
/* See: http://blogs.msdn.com/oldnewthing/archive/2003/10/08/55239.aspx */
|
||||
#define JIT_P_sizemcode_DEFAULT 64
|
||||
#else
|
||||
/* Could go as low as 4K, but the mmap() overhead would be rather high. */
|
||||
#define JIT_P_sizemcode_DEFAULT 32
|
||||
#endif
|
||||
|
||||
/* Optimization parameters and their defaults. Length is a char in octal! */
|
||||
#define JIT_PARAMDEF(_) \
|
||||
_(\010, maxtrace, 1000) /* Max. # of traces in cache. */ \
|
||||
_(\011, maxrecord, 4000) /* Max. # of recorded IR instructions. */ \
|
||||
_(\012, maxirconst, 500) /* Max. # of IR constants of a trace. */ \
|
||||
_(\007, maxside, 100) /* Max. # of side traces of a root trace. */ \
|
||||
_(\007, maxsnap, 500) /* Max. # of snapshots for a trace. */ \
|
||||
\
|
||||
_(\007, hotloop, 56) /* # of iter. to detect a hot loop/call. */ \
|
||||
_(\007, hotexit, 10) /* # of taken exits to start a side trace. */ \
|
||||
_(\007, tryside, 4) /* # of attempts to compile a side trace. */ \
|
||||
\
|
||||
_(\012, instunroll, 4) /* Max. unroll for instable loops. */ \
|
||||
_(\012, loopunroll, 15) /* Max. unroll for loop ops in side traces. */ \
|
||||
_(\012, callunroll, 3) /* Max. unroll for recursive calls. */ \
|
||||
_(\011, recunroll, 2) /* Min. unroll for true recursion. */ \
|
||||
\
|
||||
/* Size of each machine code area (in KBytes). */ \
|
||||
_(\011, sizemcode, JIT_P_sizemcode_DEFAULT) \
|
||||
/* Max. total size of all machine code areas (in KBytes). */ \
|
||||
_(\010, maxmcode, 512) \
|
||||
/* End of list. */
|
||||
|
||||
enum {
|
||||
#define JIT_PARAMENUM(len, name, value) JIT_P_##name,
|
||||
JIT_PARAMDEF(JIT_PARAMENUM)
|
||||
#undef JIT_PARAMENUM
|
||||
JIT_P__MAX
|
||||
};
|
||||
|
||||
#define JIT_PARAMSTR(len, name, value) #len #name
|
||||
#define JIT_P_STRING JIT_PARAMDEF(JIT_PARAMSTR)
|
||||
|
||||
/* Trace compiler state. */
|
||||
typedef enum {
|
||||
LJ_TRACE_IDLE, /* Trace compiler idle. */
|
||||
LJ_TRACE_ACTIVE = 0x10,
|
||||
LJ_TRACE_RECORD, /* Bytecode recording active. */
|
||||
LJ_TRACE_START, /* New trace started. */
|
||||
LJ_TRACE_END, /* End of trace. */
|
||||
LJ_TRACE_ASM, /* Assemble trace. */
|
||||
LJ_TRACE_ERR /* Trace aborted with error. */
|
||||
} TraceState;
|
||||
|
||||
/* Post-processing action. */
|
||||
typedef enum {
|
||||
LJ_POST_NONE, /* No action. */
|
||||
LJ_POST_FIXCOMP, /* Fixup comparison and emit pending guard. */
|
||||
LJ_POST_FIXGUARD, /* Fixup and emit pending guard. */
|
||||
LJ_POST_FIXGUARDSNAP, /* Fixup and emit pending guard and snapshot. */
|
||||
LJ_POST_FIXBOOL, /* Fixup boolean result. */
|
||||
LJ_POST_FIXCONST, /* Fixup constant results. */
|
||||
LJ_POST_FFRETRY /* Suppress recording of retried fast functions. */
|
||||
} PostProc;
|
||||
|
||||
/* Machine code type. */
|
||||
#if LJ_TARGET_X86ORX64
|
||||
typedef uint8_t MCode;
|
||||
#else
|
||||
typedef uint32_t MCode;
|
||||
#endif
|
||||
|
||||
/* Stack snapshot header. */
|
||||
typedef struct SnapShot {
|
||||
uint16_t mapofs; /* Offset into snapshot map. */
|
||||
IRRef1 ref; /* First IR ref for this snapshot. */
|
||||
uint8_t nslots; /* Number of valid slots. */
|
||||
uint8_t topslot; /* Maximum frame extent. */
|
||||
uint8_t nent; /* Number of compressed entries. */
|
||||
uint8_t count; /* Count of taken exits for this snapshot. */
|
||||
} SnapShot;
|
||||
|
||||
#define SNAPCOUNT_DONE 255 /* Already compiled and linked a side trace. */
|
||||
|
||||
/* Compressed snapshot entry. */
|
||||
typedef uint32_t SnapEntry;
|
||||
|
||||
#define SNAP_FRAME 0x010000 /* Frame slot. */
|
||||
#define SNAP_CONT 0x020000 /* Continuation slot. */
|
||||
#define SNAP_NORESTORE 0x040000 /* No need to restore slot. */
|
||||
#define SNAP_SOFTFPNUM 0x080000 /* Soft-float number. */
|
||||
LJ_STATIC_ASSERT(SNAP_FRAME == TREF_FRAME);
|
||||
LJ_STATIC_ASSERT(SNAP_CONT == TREF_CONT);
|
||||
|
||||
#define SNAP(slot, flags, ref) (((SnapEntry)(slot) << 24) + (flags) + (ref))
|
||||
#define SNAP_TR(slot, tr) \
|
||||
(((SnapEntry)(slot) << 24) + ((tr) & (TREF_CONT|TREF_FRAME|TREF_REFMASK)))
|
||||
#define SNAP_MKPC(pc) ((SnapEntry)u32ptr(pc))
|
||||
#define SNAP_MKFTSZ(ftsz) ((SnapEntry)(ftsz))
|
||||
#define snap_ref(sn) ((sn) & 0xffff)
|
||||
#define snap_slot(sn) ((BCReg)((sn) >> 24))
|
||||
#define snap_isframe(sn) ((sn) & SNAP_FRAME)
|
||||
#define snap_pc(sn) ((const BCIns *)(uintptr_t)(sn))
|
||||
#define snap_setref(sn, ref) (((sn) & (0xffff0000&~SNAP_NORESTORE)) | (ref))
|
||||
|
||||
/* Snapshot and exit numbers. */
|
||||
typedef uint32_t SnapNo;
|
||||
typedef uint32_t ExitNo;
|
||||
|
||||
/* Trace number. */
|
||||
typedef uint32_t TraceNo; /* Used to pass around trace numbers. */
|
||||
typedef uint16_t TraceNo1; /* Stored trace number. */
|
||||
|
||||
/* Type of link. ORDER LJ_TRLINK */
|
||||
typedef enum {
|
||||
LJ_TRLINK_NONE, /* Incomplete trace. No link, yet. */
|
||||
LJ_TRLINK_ROOT, /* Link to other root trace. */
|
||||
LJ_TRLINK_LOOP, /* Loop to same trace. */
|
||||
LJ_TRLINK_TAILREC, /* Tail-recursion. */
|
||||
LJ_TRLINK_UPREC, /* Up-recursion. */
|
||||
LJ_TRLINK_DOWNREC, /* Down-recursion. */
|
||||
LJ_TRLINK_INTERP, /* Fallback to interpreter. */
|
||||
LJ_TRLINK_RETURN /* Return to interpreter. */
|
||||
} TraceLink;
|
||||
|
||||
/* Trace object. */
|
||||
typedef struct GCtrace {
|
||||
GCHeader;
|
||||
uint8_t topslot; /* Top stack slot already checked to be allocated. */
|
||||
uint8_t linktype; /* Type of link. */
|
||||
IRRef nins; /* Next IR instruction. Biased with REF_BIAS. */
|
||||
GCRef gclist;
|
||||
IRIns *ir; /* IR instructions/constants. Biased with REF_BIAS. */
|
||||
IRRef nk; /* Lowest IR constant. Biased with REF_BIAS. */
|
||||
uint16_t nsnap; /* Number of snapshots. */
|
||||
uint16_t nsnapmap; /* Number of snapshot map elements. */
|
||||
SnapShot *snap; /* Snapshot array. */
|
||||
SnapEntry *snapmap; /* Snapshot map. */
|
||||
GCRef startpt; /* Starting prototype. */
|
||||
MRef startpc; /* Bytecode PC of starting instruction. */
|
||||
BCIns startins; /* Original bytecode of starting instruction. */
|
||||
MSize szmcode; /* Size of machine code. */
|
||||
MCode *mcode; /* Start of machine code. */
|
||||
MSize mcloop; /* Offset of loop start in machine code. */
|
||||
uint16_t nchild; /* Number of child traces (root trace only). */
|
||||
uint16_t spadjust; /* Stack pointer adjustment (offset in bytes). */
|
||||
TraceNo1 traceno; /* Trace number. */
|
||||
TraceNo1 link; /* Linked trace (or self for loops). */
|
||||
TraceNo1 root; /* Root trace of side trace (or 0 for root traces). */
|
||||
TraceNo1 nextroot; /* Next root trace for same prototype. */
|
||||
TraceNo1 nextside; /* Next side trace of same root trace. */
|
||||
uint8_t sinktags; /* Trace has SINK tags. */
|
||||
uint8_t unused1;
|
||||
#ifdef LUAJIT_USE_GDBJIT
|
||||
void *gdbjit_entry; /* GDB JIT entry. */
|
||||
#endif
|
||||
} GCtrace;
|
||||
|
||||
#define gco2trace(o) check_exp((o)->gch.gct == ~LJ_TTRACE, (GCtrace *)(o))
|
||||
#define traceref(J, n) \
|
||||
check_exp((n)>0 && (MSize)(n)<J->sizetrace, (GCtrace *)gcref(J->trace[(n)]))
|
||||
|
||||
LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCtrace, gclist));
|
||||
|
||||
static LJ_AINLINE MSize snap_nextofs(GCtrace *T, SnapShot *snap)
|
||||
{
|
||||
if (snap+1 == &T->snap[T->nsnap])
|
||||
return T->nsnapmap;
|
||||
else
|
||||
return (snap+1)->mapofs;
|
||||
}
|
||||
|
||||
/* Round-robin penalty cache for bytecodes leading to aborted traces. */
|
||||
typedef struct HotPenalty {
|
||||
MRef pc; /* Starting bytecode PC. */
|
||||
uint16_t val; /* Penalty value, i.e. hotcount start. */
|
||||
uint16_t reason; /* Abort reason (really TraceErr). */
|
||||
} HotPenalty;
|
||||
|
||||
#define PENALTY_SLOTS 64 /* Penalty cache slot. Must be a power of 2. */
|
||||
#define PENALTY_MIN (36*2) /* Minimum penalty value. */
|
||||
#define PENALTY_MAX 60000 /* Maximum penalty value. */
|
||||
#define PENALTY_RNDBITS 4 /* # of random bits to add to penalty value. */
|
||||
|
||||
/* Round-robin backpropagation cache for narrowing conversions. */
|
||||
typedef struct BPropEntry {
|
||||
IRRef1 key; /* Key: original reference. */
|
||||
IRRef1 val; /* Value: reference after conversion. */
|
||||
IRRef mode; /* Mode for this entry (currently IRCONV_*). */
|
||||
} BPropEntry;
|
||||
|
||||
/* Number of slots for the backpropagation cache. Must be a power of 2. */
|
||||
#define BPROP_SLOTS 16
|
||||
|
||||
/* Scalar evolution analysis cache. */
|
||||
typedef struct ScEvEntry {
|
||||
IRRef1 idx; /* Index reference. */
|
||||
IRRef1 start; /* Constant start reference. */
|
||||
IRRef1 stop; /* Constant stop reference. */
|
||||
IRRef1 step; /* Constant step reference. */
|
||||
IRType1 t; /* Scalar type. */
|
||||
uint8_t dir; /* Direction. 1: +, 0: -. */
|
||||
} ScEvEntry;
|
||||
|
||||
/* 128 bit SIMD constants. */
|
||||
enum {
|
||||
LJ_KSIMD_ABS,
|
||||
LJ_KSIMD_NEG,
|
||||
LJ_KSIMD__MAX
|
||||
};
|
||||
|
||||
/* Get 16 byte aligned pointer to SIMD constant. */
|
||||
#define LJ_KSIMD(J, n) \
|
||||
((TValue *)(((intptr_t)&J->ksimd[2*(n)] + 15) & ~(intptr_t)15))
|
||||
|
||||
/* Set/reset flag to activate the SPLIT pass for the current trace. */
|
||||
#if LJ_SOFTFP || (LJ_32 && LJ_HASFFI)
|
||||
#define lj_needsplit(J) (J->needsplit = 1)
|
||||
#define lj_resetsplit(J) (J->needsplit = 0)
|
||||
#else
|
||||
#define lj_needsplit(J) UNUSED(J)
|
||||
#define lj_resetsplit(J) UNUSED(J)
|
||||
#endif
|
||||
|
||||
/* Fold state is used to fold instructions on-the-fly. */
|
||||
typedef struct FoldState {
|
||||
IRIns ins; /* Currently emitted instruction. */
|
||||
IRIns left; /* Instruction referenced by left operand. */
|
||||
IRIns right; /* Instruction referenced by right operand. */
|
||||
} FoldState;
|
||||
|
||||
/* JIT compiler state. */
|
||||
typedef struct jit_State {
|
||||
GCtrace cur; /* Current trace. */
|
||||
|
||||
lua_State *L; /* Current Lua state. */
|
||||
const BCIns *pc; /* Current PC. */
|
||||
GCfunc *fn; /* Current function. */
|
||||
GCproto *pt; /* Current prototype. */
|
||||
TRef *base; /* Current frame base, points into J->slots. */
|
||||
|
||||
uint32_t flags; /* JIT engine flags. */
|
||||
BCReg maxslot; /* Relative to baseslot. */
|
||||
BCReg baseslot; /* Current frame base, offset into J->slots. */
|
||||
|
||||
uint8_t mergesnap; /* Allowed to merge with next snapshot. */
|
||||
uint8_t needsnap; /* Need snapshot before recording next bytecode. */
|
||||
IRType1 guardemit; /* Accumulated IRT_GUARD for emitted instructions. */
|
||||
uint8_t bcskip; /* Number of bytecode instructions to skip. */
|
||||
|
||||
FoldState fold; /* Fold state. */
|
||||
|
||||
const BCIns *bc_min; /* Start of allowed bytecode range for root trace. */
|
||||
MSize bc_extent; /* Extent of the range. */
|
||||
|
||||
TraceState state; /* Trace compiler state. */
|
||||
|
||||
int32_t instunroll; /* Unroll counter for instable loops. */
|
||||
int32_t loopunroll; /* Unroll counter for loop ops in side traces. */
|
||||
int32_t tailcalled; /* Number of successive tailcalls. */
|
||||
int32_t framedepth; /* Current frame depth. */
|
||||
int32_t retdepth; /* Return frame depth (count of RETF). */
|
||||
|
||||
MRef k64; /* Pointer to chained array of 64 bit constants. */
|
||||
TValue ksimd[LJ_KSIMD__MAX*2+1]; /* 16 byte aligned SIMD constants. */
|
||||
|
||||
IRIns *irbuf; /* Temp. IR instruction buffer. Biased with REF_BIAS. */
|
||||
IRRef irtoplim; /* Upper limit of instuction buffer (biased). */
|
||||
IRRef irbotlim; /* Lower limit of instuction buffer (biased). */
|
||||
IRRef loopref; /* Last loop reference or ref of final LOOP (or 0). */
|
||||
|
||||
MSize sizesnap; /* Size of temp. snapshot buffer. */
|
||||
SnapShot *snapbuf; /* Temp. snapshot buffer. */
|
||||
SnapEntry *snapmapbuf; /* Temp. snapshot map buffer. */
|
||||
MSize sizesnapmap; /* Size of temp. snapshot map buffer. */
|
||||
|
||||
PostProc postproc; /* Required post-processing after execution. */
|
||||
#if LJ_SOFTFP || (LJ_32 && LJ_HASFFI)
|
||||
int needsplit; /* Need SPLIT pass. */
|
||||
#endif
|
||||
|
||||
GCRef *trace; /* Array of traces. */
|
||||
TraceNo freetrace; /* Start of scan for next free trace. */
|
||||
MSize sizetrace; /* Size of trace array. */
|
||||
|
||||
IRRef1 chain[IR__MAX]; /* IR instruction skip-list chain anchors. */
|
||||
TRef slot[LJ_MAX_JSLOTS+LJ_STACK_EXTRA]; /* Stack slot map. */
|
||||
|
||||
int32_t param[JIT_P__MAX]; /* JIT engine parameters. */
|
||||
|
||||
MCode *exitstubgroup[LJ_MAX_EXITSTUBGR]; /* Exit stub group addresses. */
|
||||
|
||||
HotPenalty penalty[PENALTY_SLOTS]; /* Penalty slots. */
|
||||
uint32_t penaltyslot; /* Round-robin index into penalty slots. */
|
||||
uint32_t prngstate; /* PRNG state. */
|
||||
|
||||
BPropEntry bpropcache[BPROP_SLOTS]; /* Backpropagation cache slots. */
|
||||
uint32_t bpropslot; /* Round-robin index into bpropcache slots. */
|
||||
|
||||
ScEvEntry scev; /* Scalar evolution analysis cache slots. */
|
||||
|
||||
const BCIns *startpc; /* Bytecode PC of starting instruction. */
|
||||
TraceNo parent; /* Parent of current side trace (0 for root traces). */
|
||||
ExitNo exitno; /* Exit number in parent of current side trace. */
|
||||
|
||||
BCIns *patchpc; /* PC for pending re-patch. */
|
||||
BCIns patchins; /* Instruction for pending re-patch. */
|
||||
|
||||
int mcprot; /* Protection of current mcode area. */
|
||||
MCode *mcarea; /* Base of current mcode area. */
|
||||
MCode *mctop; /* Top of current mcode area. */
|
||||
MCode *mcbot; /* Bottom of current mcode area. */
|
||||
size_t szmcarea; /* Size of current mcode area. */
|
||||
size_t szallmcarea; /* Total size of all allocated mcode areas. */
|
||||
|
||||
TValue errinfo; /* Additional info element for trace errors. */
|
||||
}
|
||||
#if LJ_TARGET_ARM
|
||||
LJ_ALIGN(16) /* For DISPATCH-relative addresses in assembler part. */
|
||||
#endif
|
||||
jit_State;
|
||||
|
||||
/* Trivial PRNG e.g. used for penalty randomization. */
|
||||
static LJ_AINLINE uint32_t LJ_PRNG_BITS(jit_State *J, int bits)
|
||||
{
|
||||
/* Yes, this LCG is very weak, but that doesn't matter for our use case. */
|
||||
J->prngstate = J->prngstate * 1103515245 + 12345;
|
||||
return J->prngstate >> (32-bits);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,481 +0,0 @@
|
||||
/*
|
||||
** Lexical analyzer.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lj_lex_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_tab.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lualib.h"
|
||||
#endif
|
||||
#include "lj_state.h"
|
||||
#include "lj_lex.h"
|
||||
#include "lj_parse.h"
|
||||
#include "lj_char.h"
|
||||
#include "lj_strscan.h"
|
||||
|
||||
/* Lua lexer token names. */
|
||||
static const char *const tokennames[] = {
|
||||
#define TKSTR1(name) #name,
|
||||
#define TKSTR2(name, sym) #sym,
|
||||
TKDEF(TKSTR1, TKSTR2)
|
||||
#undef TKSTR1
|
||||
#undef TKSTR2
|
||||
NULL
|
||||
};
|
||||
|
||||
/* -- Buffer handling ----------------------------------------------------- */
|
||||
|
||||
#define char2int(c) ((int)(uint8_t)(c))
|
||||
#define next(ls) \
|
||||
(ls->current = (ls->n--) > 0 ? char2int(*ls->p++) : fillbuf(ls))
|
||||
#define save_and_next(ls) (save(ls, ls->current), next(ls))
|
||||
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
|
||||
#define END_OF_STREAM (-1)
|
||||
|
||||
static int fillbuf(LexState *ls)
|
||||
{
|
||||
size_t sz;
|
||||
const char *buf = ls->rfunc(ls->L, ls->rdata, &sz);
|
||||
if (buf == NULL || sz == 0) return END_OF_STREAM;
|
||||
ls->n = (MSize)sz - 1;
|
||||
ls->p = buf;
|
||||
return char2int(*(ls->p++));
|
||||
}
|
||||
|
||||
static LJ_NOINLINE void save_grow(LexState *ls, int c)
|
||||
{
|
||||
MSize newsize;
|
||||
if (ls->sb.sz >= LJ_MAX_STR/2)
|
||||
lj_lex_error(ls, 0, LJ_ERR_XELEM);
|
||||
newsize = ls->sb.sz * 2;
|
||||
lj_str_resizebuf(ls->L, &ls->sb, newsize);
|
||||
ls->sb.buf[ls->sb.n++] = (char)c;
|
||||
}
|
||||
|
||||
static LJ_AINLINE void save(LexState *ls, int c)
|
||||
{
|
||||
if (LJ_UNLIKELY(ls->sb.n + 1 > ls->sb.sz))
|
||||
save_grow(ls, c);
|
||||
else
|
||||
ls->sb.buf[ls->sb.n++] = (char)c;
|
||||
}
|
||||
|
||||
static void inclinenumber(LexState *ls)
|
||||
{
|
||||
int old = ls->current;
|
||||
lua_assert(currIsNewline(ls));
|
||||
next(ls); /* skip `\n' or `\r' */
|
||||
if (currIsNewline(ls) && ls->current != old)
|
||||
next(ls); /* skip `\n\r' or `\r\n' */
|
||||
if (++ls->linenumber >= LJ_MAX_LINE)
|
||||
lj_lex_error(ls, ls->token, LJ_ERR_XLINES);
|
||||
}
|
||||
|
||||
/* -- Scanner for terminals ----------------------------------------------- */
|
||||
|
||||
/* Parse a number literal. */
|
||||
static void lex_number(LexState *ls, TValue *tv)
|
||||
{
|
||||
StrScanFmt fmt;
|
||||
int c, xp = 'e';
|
||||
lua_assert(lj_char_isdigit(ls->current));
|
||||
if ((c = ls->current) == '0') {
|
||||
save_and_next(ls);
|
||||
if ((ls->current | 0x20) == 'x') xp = 'p';
|
||||
}
|
||||
while (lj_char_isident(ls->current) || ls->current == '.' ||
|
||||
((ls->current == '-' || ls->current == '+') && (c | 0x20) == xp)) {
|
||||
c = ls->current;
|
||||
save_and_next(ls);
|
||||
}
|
||||
save(ls, '\0');
|
||||
fmt = lj_strscan_scan((const uint8_t *)ls->sb.buf, tv,
|
||||
(LJ_DUALNUM ? STRSCAN_OPT_TOINT : STRSCAN_OPT_TONUM) |
|
||||
(LJ_HASFFI ? (STRSCAN_OPT_LL|STRSCAN_OPT_IMAG) : 0));
|
||||
if (LJ_DUALNUM && fmt == STRSCAN_INT) {
|
||||
setitype(tv, LJ_TISNUM);
|
||||
} else if (fmt == STRSCAN_NUM) {
|
||||
/* Already in correct format. */
|
||||
#if LJ_HASFFI
|
||||
} else if (fmt != STRSCAN_ERROR) {
|
||||
lua_State *L = ls->L;
|
||||
GCcdata *cd;
|
||||
lua_assert(fmt == STRSCAN_I64 || fmt == STRSCAN_U64 || fmt == STRSCAN_IMAG);
|
||||
if (!ctype_ctsG(G(L))) {
|
||||
ptrdiff_t oldtop = savestack(L, L->top);
|
||||
luaopen_ffi(L); /* Load FFI library on-demand. */
|
||||
L->top = restorestack(L, oldtop);
|
||||
}
|
||||
if (fmt == STRSCAN_IMAG) {
|
||||
cd = lj_cdata_new_(L, CTID_COMPLEX_DOUBLE, 2*sizeof(double));
|
||||
((double *)cdataptr(cd))[0] = 0;
|
||||
((double *)cdataptr(cd))[1] = numV(tv);
|
||||
} else {
|
||||
cd = lj_cdata_new_(L, fmt==STRSCAN_I64 ? CTID_INT64 : CTID_UINT64, 8);
|
||||
*(uint64_t *)cdataptr(cd) = tv->u64;
|
||||
}
|
||||
lj_parse_keepcdata(ls, tv, cd);
|
||||
#endif
|
||||
} else {
|
||||
lua_assert(fmt == STRSCAN_ERROR);
|
||||
lj_lex_error(ls, TK_number, LJ_ERR_XNUMBER);
|
||||
}
|
||||
}
|
||||
|
||||
static int skip_sep(LexState *ls)
|
||||
{
|
||||
int count = 0;
|
||||
int s = ls->current;
|
||||
lua_assert(s == '[' || s == ']');
|
||||
save_and_next(ls);
|
||||
while (ls->current == '=') {
|
||||
save_and_next(ls);
|
||||
count++;
|
||||
}
|
||||
return (ls->current == s) ? count : (-count) - 1;
|
||||
}
|
||||
|
||||
static void read_long_string(LexState *ls, TValue *tv, int sep)
|
||||
{
|
||||
save_and_next(ls); /* skip 2nd `[' */
|
||||
if (currIsNewline(ls)) /* string starts with a newline? */
|
||||
inclinenumber(ls); /* skip it */
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case END_OF_STREAM:
|
||||
lj_lex_error(ls, TK_eof, tv ? LJ_ERR_XLSTR : LJ_ERR_XLCOM);
|
||||
break;
|
||||
case ']':
|
||||
if (skip_sep(ls) == sep) {
|
||||
save_and_next(ls); /* skip 2nd `]' */
|
||||
goto endloop;
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
save(ls, '\n');
|
||||
inclinenumber(ls);
|
||||
if (!tv) lj_str_resetbuf(&ls->sb); /* avoid wasting space */
|
||||
break;
|
||||
default:
|
||||
if (tv) save_and_next(ls);
|
||||
else next(ls);
|
||||
break;
|
||||
}
|
||||
} endloop:
|
||||
if (tv) {
|
||||
GCstr *str = lj_parse_keepstr(ls, ls->sb.buf + (2 + (MSize)sep),
|
||||
ls->sb.n - 2*(2 + (MSize)sep));
|
||||
setstrV(ls->L, tv, str);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_string(LexState *ls, int delim, TValue *tv)
|
||||
{
|
||||
save_and_next(ls);
|
||||
while (ls->current != delim) {
|
||||
switch (ls->current) {
|
||||
case END_OF_STREAM:
|
||||
lj_lex_error(ls, TK_eof, LJ_ERR_XSTR);
|
||||
continue;
|
||||
case '\n':
|
||||
case '\r':
|
||||
lj_lex_error(ls, TK_string, LJ_ERR_XSTR);
|
||||
continue;
|
||||
case '\\': {
|
||||
int c = next(ls); /* Skip the '\\'. */
|
||||
switch (c) {
|
||||
case 'a': c = '\a'; break;
|
||||
case 'b': c = '\b'; break;
|
||||
case 'f': c = '\f'; break;
|
||||
case 'n': c = '\n'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 't': c = '\t'; break;
|
||||
case 'v': c = '\v'; break;
|
||||
case 'x': /* Hexadecimal escape '\xXX'. */
|
||||
c = (next(ls) & 15u) << 4;
|
||||
if (!lj_char_isdigit(ls->current)) {
|
||||
if (!lj_char_isxdigit(ls->current)) goto err_xesc;
|
||||
c += 9 << 4;
|
||||
}
|
||||
c += (next(ls) & 15u);
|
||||
if (!lj_char_isdigit(ls->current)) {
|
||||
if (!lj_char_isxdigit(ls->current)) goto err_xesc;
|
||||
c += 9;
|
||||
}
|
||||
break;
|
||||
case 'z': /* Skip whitespace. */
|
||||
next(ls);
|
||||
while (lj_char_isspace(ls->current))
|
||||
if (currIsNewline(ls)) inclinenumber(ls); else next(ls);
|
||||
continue;
|
||||
case '\n': case '\r': save(ls, '\n'); inclinenumber(ls); continue;
|
||||
case '\\': case '\"': case '\'': break;
|
||||
case END_OF_STREAM: continue;
|
||||
default:
|
||||
if (!lj_char_isdigit(c))
|
||||
goto err_xesc;
|
||||
c -= '0'; /* Decimal escape '\ddd'. */
|
||||
if (lj_char_isdigit(next(ls))) {
|
||||
c = c*10 + (ls->current - '0');
|
||||
if (lj_char_isdigit(next(ls))) {
|
||||
c = c*10 + (ls->current - '0');
|
||||
if (c > 255) {
|
||||
err_xesc:
|
||||
lj_lex_error(ls, TK_string, LJ_ERR_XESC);
|
||||
}
|
||||
next(ls);
|
||||
}
|
||||
}
|
||||
save(ls, c);
|
||||
continue;
|
||||
}
|
||||
save(ls, c);
|
||||
next(ls);
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
save_and_next(ls);
|
||||
break;
|
||||
}
|
||||
}
|
||||
save_and_next(ls); /* skip delimiter */
|
||||
setstrV(ls->L, tv, lj_parse_keepstr(ls, ls->sb.buf + 1, ls->sb.n - 2));
|
||||
}
|
||||
|
||||
/* -- Main lexical scanner ------------------------------------------------ */
|
||||
|
||||
static int llex(LexState *ls, TValue *tv)
|
||||
{
|
||||
lj_str_resetbuf(&ls->sb);
|
||||
for (;;) {
|
||||
if (lj_char_isident(ls->current)) {
|
||||
GCstr *s;
|
||||
if (lj_char_isdigit(ls->current)) { /* Numeric literal. */
|
||||
lex_number(ls, tv);
|
||||
return TK_number;
|
||||
}
|
||||
/* Identifier or reserved word. */
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (lj_char_isident(ls->current));
|
||||
s = lj_parse_keepstr(ls, ls->sb.buf, ls->sb.n);
|
||||
setstrV(ls->L, tv, s);
|
||||
if (s->reserved > 0) /* Reserved word? */
|
||||
return TK_OFS + s->reserved;
|
||||
return TK_name;
|
||||
}
|
||||
switch (ls->current) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
inclinenumber(ls);
|
||||
continue;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\f':
|
||||
next(ls);
|
||||
continue;
|
||||
case '-':
|
||||
next(ls);
|
||||
if (ls->current != '-') return '-';
|
||||
/* else is a comment */
|
||||
next(ls);
|
||||
if (ls->current == '[') {
|
||||
int sep = skip_sep(ls);
|
||||
lj_str_resetbuf(&ls->sb); /* `skip_sep' may dirty the buffer */
|
||||
if (sep >= 0) {
|
||||
read_long_string(ls, NULL, sep); /* long comment */
|
||||
lj_str_resetbuf(&ls->sb);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* else short comment */
|
||||
while (!currIsNewline(ls) && ls->current != END_OF_STREAM)
|
||||
next(ls);
|
||||
continue;
|
||||
case '[': {
|
||||
int sep = skip_sep(ls);
|
||||
if (sep >= 0) {
|
||||
read_long_string(ls, tv, sep);
|
||||
return TK_string;
|
||||
} else if (sep == -1) {
|
||||
return '[';
|
||||
} else {
|
||||
lj_lex_error(ls, TK_string, LJ_ERR_XLDELIM);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
case '=':
|
||||
next(ls);
|
||||
if (ls->current != '=') return '='; else { next(ls); return TK_eq; }
|
||||
case '<':
|
||||
next(ls);
|
||||
if (ls->current != '=') return '<'; else { next(ls); return TK_le; }
|
||||
case '>':
|
||||
next(ls);
|
||||
if (ls->current != '=') return '>'; else { next(ls); return TK_ge; }
|
||||
case '~':
|
||||
next(ls);
|
||||
if (ls->current != '=') return '~'; else { next(ls); return TK_ne; }
|
||||
case ':':
|
||||
next(ls);
|
||||
if (ls->current != ':') return ':'; else { next(ls); return TK_label; }
|
||||
case '"':
|
||||
case '\'':
|
||||
read_string(ls, ls->current, tv);
|
||||
return TK_string;
|
||||
case '.':
|
||||
save_and_next(ls);
|
||||
if (ls->current == '.') {
|
||||
next(ls);
|
||||
if (ls->current == '.') {
|
||||
next(ls);
|
||||
return TK_dots; /* ... */
|
||||
}
|
||||
return TK_concat; /* .. */
|
||||
} else if (!lj_char_isdigit(ls->current)) {
|
||||
return '.';
|
||||
} else {
|
||||
lex_number(ls, tv);
|
||||
return TK_number;
|
||||
}
|
||||
case END_OF_STREAM:
|
||||
return TK_eof;
|
||||
default: {
|
||||
int c = ls->current;
|
||||
next(ls);
|
||||
return c; /* Single-char tokens (+ - / ...). */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Lexer API ----------------------------------------------------------- */
|
||||
|
||||
/* Setup lexer state. */
|
||||
int lj_lex_setup(lua_State *L, LexState *ls)
|
||||
{
|
||||
int header = 0;
|
||||
ls->L = L;
|
||||
ls->fs = NULL;
|
||||
ls->n = 0;
|
||||
ls->p = NULL;
|
||||
ls->vstack = NULL;
|
||||
ls->sizevstack = 0;
|
||||
ls->vtop = 0;
|
||||
ls->bcstack = NULL;
|
||||
ls->sizebcstack = 0;
|
||||
ls->lookahead = TK_eof; /* No look-ahead token. */
|
||||
ls->linenumber = 1;
|
||||
ls->lastline = 1;
|
||||
lj_str_resizebuf(ls->L, &ls->sb, LJ_MIN_SBUF);
|
||||
next(ls); /* Read-ahead first char. */
|
||||
if (ls->current == 0xef && ls->n >= 2 && char2int(ls->p[0]) == 0xbb &&
|
||||
char2int(ls->p[1]) == 0xbf) { /* Skip UTF-8 BOM (if buffered). */
|
||||
ls->n -= 2;
|
||||
ls->p += 2;
|
||||
next(ls);
|
||||
header = 1;
|
||||
}
|
||||
if (ls->current == '#') { /* Skip POSIX #! header line. */
|
||||
do {
|
||||
next(ls);
|
||||
if (ls->current == END_OF_STREAM) return 0;
|
||||
} while (!currIsNewline(ls));
|
||||
inclinenumber(ls);
|
||||
header = 1;
|
||||
}
|
||||
if (ls->current == LUA_SIGNATURE[0]) { /* Bytecode dump. */
|
||||
if (header) {
|
||||
/*
|
||||
** Loading bytecode with an extra header is disabled for security
|
||||
** reasons. This may circumvent the usual check for bytecode vs.
|
||||
** Lua code by looking at the first char. Since this is a potential
|
||||
** security violation no attempt is made to echo the chunkname either.
|
||||
*/
|
||||
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_BCBAD));
|
||||
lj_err_throw(L, LUA_ERRSYNTAX);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup lexer state. */
|
||||
void lj_lex_cleanup(lua_State *L, LexState *ls)
|
||||
{
|
||||
global_State *g = G(L);
|
||||
lj_mem_freevec(g, ls->bcstack, ls->sizebcstack, BCInsLine);
|
||||
lj_mem_freevec(g, ls->vstack, ls->sizevstack, VarInfo);
|
||||
lj_str_freebuf(g, &ls->sb);
|
||||
}
|
||||
|
||||
void lj_lex_next(LexState *ls)
|
||||
{
|
||||
ls->lastline = ls->linenumber;
|
||||
if (LJ_LIKELY(ls->lookahead == TK_eof)) { /* No lookahead token? */
|
||||
ls->token = llex(ls, &ls->tokenval); /* Get next token. */
|
||||
} else { /* Otherwise return lookahead token. */
|
||||
ls->token = ls->lookahead;
|
||||
ls->lookahead = TK_eof;
|
||||
ls->tokenval = ls->lookaheadval;
|
||||
}
|
||||
}
|
||||
|
||||
LexToken lj_lex_lookahead(LexState *ls)
|
||||
{
|
||||
lua_assert(ls->lookahead == TK_eof);
|
||||
ls->lookahead = llex(ls, &ls->lookaheadval);
|
||||
return ls->lookahead;
|
||||
}
|
||||
|
||||
const char *lj_lex_token2str(LexState *ls, LexToken token)
|
||||
{
|
||||
if (token > TK_OFS)
|
||||
return tokennames[token-TK_OFS-1];
|
||||
else if (!lj_char_iscntrl(token))
|
||||
return lj_str_pushf(ls->L, "%c", token);
|
||||
else
|
||||
return lj_str_pushf(ls->L, "char(%d)", token);
|
||||
}
|
||||
|
||||
void lj_lex_error(LexState *ls, LexToken token, ErrMsg em, ...)
|
||||
{
|
||||
const char *tok;
|
||||
va_list argp;
|
||||
if (token == 0) {
|
||||
tok = NULL;
|
||||
} else if (token == TK_name || token == TK_string || token == TK_number) {
|
||||
save(ls, '\0');
|
||||
tok = ls->sb.buf;
|
||||
} else {
|
||||
tok = lj_lex_token2str(ls, token);
|
||||
}
|
||||
va_start(argp, em);
|
||||
lj_err_lex(ls->L, ls->chunkname, tok, ls->linenumber, em, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
void lj_lex_init(lua_State *L)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < TK_RESERVED; i++) {
|
||||
GCstr *s = lj_str_newz(L, tokennames[i]);
|
||||
fixstring(s); /* Reserved words are never collected. */
|
||||
s->reserved = (uint8_t)(i+1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
** Library function support.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_lib_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_func.h"
|
||||
#include "lj_bc.h"
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_vm.h"
|
||||
#include "lj_strscan.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* -- Library initialization ---------------------------------------------- */
|
||||
|
||||
static GCtab *lib_create_table(lua_State *L, const char *libname, int hsize)
|
||||
{
|
||||
if (libname) {
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
|
||||
lua_getfield(L, -1, libname);
|
||||
if (!tvistab(L->top-1)) {
|
||||
L->top--;
|
||||
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, hsize) != NULL)
|
||||
lj_err_callerv(L, LJ_ERR_BADMODN, libname);
|
||||
settabV(L, L->top, tabV(L->top-1));
|
||||
L->top++;
|
||||
lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
|
||||
}
|
||||
L->top--;
|
||||
settabV(L, L->top-1, tabV(L->top));
|
||||
} else {
|
||||
lua_createtable(L, 0, hsize);
|
||||
}
|
||||
return tabV(L->top-1);
|
||||
}
|
||||
|
||||
void lj_lib_register(lua_State *L, const char *libname,
|
||||
const uint8_t *p, const lua_CFunction *cf)
|
||||
{
|
||||
GCtab *env = tabref(L->env);
|
||||
GCfunc *ofn = NULL;
|
||||
int ffid = *p++;
|
||||
BCIns *bcff = &L2GG(L)->bcff[*p++];
|
||||
GCtab *tab = lib_create_table(L, libname, *p++);
|
||||
ptrdiff_t tpos = L->top - L->base;
|
||||
|
||||
/* Avoid barriers further down. */
|
||||
lj_gc_anybarriert(L, tab);
|
||||
tab->nomm = 0;
|
||||
|
||||
for (;;) {
|
||||
uint32_t tag = *p++;
|
||||
MSize len = tag & LIBINIT_LENMASK;
|
||||
tag &= LIBINIT_TAGMASK;
|
||||
if (tag != LIBINIT_STRING) {
|
||||
const char *name;
|
||||
MSize nuv = (MSize)(L->top - L->base - tpos);
|
||||
GCfunc *fn = lj_func_newC(L, nuv, env);
|
||||
if (nuv) {
|
||||
L->top = L->base + tpos;
|
||||
memcpy(fn->c.upvalue, L->top, sizeof(TValue)*nuv);
|
||||
}
|
||||
fn->c.ffid = (uint8_t)(ffid++);
|
||||
name = (const char *)p;
|
||||
p += len;
|
||||
if (tag == LIBINIT_CF)
|
||||
setmref(fn->c.pc, &G(L)->bc_cfunc_int);
|
||||
else
|
||||
setmref(fn->c.pc, bcff++);
|
||||
if (tag == LIBINIT_ASM_)
|
||||
fn->c.f = ofn->c.f; /* Copy handler from previous function. */
|
||||
else
|
||||
fn->c.f = *cf++; /* Get cf or handler from C function table. */
|
||||
if (len) {
|
||||
/* NOBARRIER: See above for common barrier. */
|
||||
setfuncV(L, lj_tab_setstr(L, tab, lj_str_new(L, name, len)), fn);
|
||||
}
|
||||
ofn = fn;
|
||||
} else {
|
||||
switch (tag | len) {
|
||||
case LIBINIT_SET:
|
||||
L->top -= 2;
|
||||
if (tvisstr(L->top+1) && strV(L->top+1)->len == 0)
|
||||
env = tabV(L->top);
|
||||
else /* NOBARRIER: See above for common barrier. */
|
||||
copyTV(L, lj_tab_set(L, tab, L->top+1), L->top);
|
||||
break;
|
||||
case LIBINIT_NUMBER:
|
||||
memcpy(&L->top->n, p, sizeof(double));
|
||||
L->top++;
|
||||
p += sizeof(double);
|
||||
break;
|
||||
case LIBINIT_COPY:
|
||||
copyTV(L, L->top, L->top - *p++);
|
||||
L->top++;
|
||||
break;
|
||||
case LIBINIT_LASTCL:
|
||||
setfuncV(L, L->top++, ofn);
|
||||
break;
|
||||
case LIBINIT_FFID:
|
||||
ffid++;
|
||||
break;
|
||||
case LIBINIT_END:
|
||||
return;
|
||||
default:
|
||||
setstrV(L, L->top++, lj_str_new(L, (const char *)p, len));
|
||||
p += len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Type checks --------------------------------------------------------- */
|
||||
|
||||
TValue *lj_lib_checkany(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (o >= L->top)
|
||||
lj_err_arg(L, narg, LJ_ERR_NOVAL);
|
||||
return o;
|
||||
}
|
||||
|
||||
GCstr *lj_lib_checkstr(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (o < L->top) {
|
||||
if (LJ_LIKELY(tvisstr(o))) {
|
||||
return strV(o);
|
||||
} else if (tvisnumber(o)) {
|
||||
GCstr *s = lj_str_fromnumber(L, o);
|
||||
setstrV(L, o, s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
lj_err_argt(L, narg, LUA_TSTRING);
|
||||
return NULL; /* unreachable */
|
||||
}
|
||||
|
||||
GCstr *lj_lib_optstr(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
return (o < L->top && !tvisnil(o)) ? lj_lib_checkstr(L, narg) : NULL;
|
||||
}
|
||||
|
||||
#if LJ_DUALNUM
|
||||
void lj_lib_checknumber(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && lj_strscan_numberobj(o)))
|
||||
lj_err_argt(L, narg, LUA_TNUMBER);
|
||||
}
|
||||
#endif
|
||||
|
||||
lua_Number lj_lib_checknum(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top &&
|
||||
(tvisnumber(o) || (tvisstr(o) && lj_strscan_num(strV(o), o)))))
|
||||
lj_err_argt(L, narg, LUA_TNUMBER);
|
||||
if (LJ_UNLIKELY(tvisint(o))) {
|
||||
lua_Number n = (lua_Number)intV(o);
|
||||
setnumV(o, n);
|
||||
return n;
|
||||
} else {
|
||||
return numV(o);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t lj_lib_checkint(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && lj_strscan_numberobj(o)))
|
||||
lj_err_argt(L, narg, LUA_TNUMBER);
|
||||
if (LJ_LIKELY(tvisint(o))) {
|
||||
return intV(o);
|
||||
} else {
|
||||
int32_t i = lj_num2int(numV(o));
|
||||
if (LJ_DUALNUM) setintV(o, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t lj_lib_optint(lua_State *L, int narg, int32_t def)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
return (o < L->top && !tvisnil(o)) ? lj_lib_checkint(L, narg) : def;
|
||||
}
|
||||
|
||||
int32_t lj_lib_checkbit(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && lj_strscan_numberobj(o)))
|
||||
lj_err_argt(L, narg, LUA_TNUMBER);
|
||||
if (LJ_LIKELY(tvisint(o))) {
|
||||
return intV(o);
|
||||
} else {
|
||||
int32_t i = lj_num2bit(numV(o));
|
||||
if (LJ_DUALNUM) setintV(o, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
GCfunc *lj_lib_checkfunc(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && tvisfunc(o)))
|
||||
lj_err_argt(L, narg, LUA_TFUNCTION);
|
||||
return funcV(o);
|
||||
}
|
||||
|
||||
GCtab *lj_lib_checktab(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && tvistab(o)))
|
||||
lj_err_argt(L, narg, LUA_TTABLE);
|
||||
return tabV(o);
|
||||
}
|
||||
|
||||
GCtab *lj_lib_checktabornil(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (o < L->top) {
|
||||
if (tvistab(o))
|
||||
return tabV(o);
|
||||
else if (tvisnil(o))
|
||||
return NULL;
|
||||
}
|
||||
lj_err_arg(L, narg, LJ_ERR_NOTABN);
|
||||
return NULL; /* unreachable */
|
||||
}
|
||||
|
||||
int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst)
|
||||
{
|
||||
GCstr *s = def >= 0 ? lj_lib_optstr(L, narg) : lj_lib_checkstr(L, narg);
|
||||
if (s) {
|
||||
const char *opt = strdata(s);
|
||||
MSize len = s->len;
|
||||
int i;
|
||||
for (i = 0; *(const uint8_t *)lst; i++) {
|
||||
if (*(const uint8_t *)lst == len && memcmp(opt, lst+1, len) == 0)
|
||||
return i;
|
||||
lst += 1+*(const uint8_t *)lst;
|
||||
}
|
||||
lj_err_argv(L, narg, LJ_ERR_INVOPTM, opt);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
** Machine code management.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_mcode_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_gc.h"
|
||||
#include "lj_jit.h"
|
||||
#include "lj_mcode.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_dispatch.h"
|
||||
#endif
|
||||
#if LJ_HASJIT || LJ_HASFFI
|
||||
#include "lj_vm.h"
|
||||
#endif
|
||||
|
||||
/* -- OS-specific functions ----------------------------------------------- */
|
||||
|
||||
#if LJ_HASJIT || LJ_HASFFI
|
||||
|
||||
/* Define this if you want to run LuaJIT with Valgrind. */
|
||||
#ifdef LUAJIT_USE_VALGRIND
|
||||
#include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_IOS
|
||||
void sys_icache_invalidate(void *start, size_t len);
|
||||
#endif
|
||||
|
||||
/* Synchronize data/instruction cache. */
|
||||
void lj_mcode_sync(void *start, void *end)
|
||||
{
|
||||
#ifdef LUAJIT_USE_VALGRIND
|
||||
VALGRIND_DISCARD_TRANSLATIONS(start, (char *)end-(char *)start);
|
||||
#endif
|
||||
#if LJ_TARGET_X86ORX64
|
||||
UNUSED(start); UNUSED(end);
|
||||
#elif LJ_TARGET_IOS
|
||||
sys_icache_invalidate(start, (char *)end-(char *)start);
|
||||
#elif LJ_TARGET_PPC
|
||||
lj_vm_cachesync(start, end);
|
||||
#elif defined(__GNUC__)
|
||||
__clear_cache(start, end);
|
||||
#else
|
||||
#error "Missing builtin to flush instruction cache"
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if LJ_HASJIT
|
||||
|
||||
#if LJ_TARGET_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#define MCPROT_RW PAGE_READWRITE
|
||||
#define MCPROT_RX PAGE_EXECUTE_READ
|
||||
#define MCPROT_RWX PAGE_EXECUTE_READWRITE
|
||||
|
||||
static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, DWORD prot)
|
||||
{
|
||||
void *p = VirtualAlloc((void *)hint, sz,
|
||||
MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, prot);
|
||||
if (!p && !hint)
|
||||
lj_trace_err(J, LJ_TRERR_MCODEAL);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void mcode_free(jit_State *J, void *p, size_t sz)
|
||||
{
|
||||
UNUSED(J); UNUSED(sz);
|
||||
VirtualFree(p, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
static void mcode_setprot(void *p, size_t sz, DWORD prot)
|
||||
{
|
||||
DWORD oprot;
|
||||
VirtualProtect(p, sz, prot, &oprot);
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_POSIX
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifndef MAP_ANONYMOUS
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
#define MCPROT_RW (PROT_READ|PROT_WRITE)
|
||||
#define MCPROT_RX (PROT_READ|PROT_EXEC)
|
||||
#define MCPROT_RWX (PROT_READ|PROT_WRITE|PROT_EXEC)
|
||||
|
||||
static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, int prot)
|
||||
{
|
||||
void *p = mmap((void *)hint, sz, prot, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
if (!hint) lj_trace_err(J, LJ_TRERR_MCODEAL);
|
||||
p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void mcode_free(jit_State *J, void *p, size_t sz)
|
||||
{
|
||||
UNUSED(J);
|
||||
munmap(p, sz);
|
||||
}
|
||||
|
||||
static void mcode_setprot(void *p, size_t sz, int prot)
|
||||
{
|
||||
mprotect(p, sz, prot);
|
||||
}
|
||||
|
||||
#elif LJ_64
|
||||
|
||||
#error "Missing OS support for explicit placement of executable memory"
|
||||
|
||||
#else
|
||||
|
||||
/* Fallback allocator. This will fail if memory is not executable by default. */
|
||||
#define LUAJIT_UNPROTECT_MCODE
|
||||
#define MCPROT_RW 0
|
||||
#define MCPROT_RX 0
|
||||
#define MCPROT_RWX 0
|
||||
|
||||
static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, int prot)
|
||||
{
|
||||
UNUSED(hint); UNUSED(prot);
|
||||
return lj_mem_new(J->L, sz);
|
||||
}
|
||||
|
||||
static void mcode_free(jit_State *J, void *p, size_t sz)
|
||||
{
|
||||
lj_mem_free(J2G(J), p, sz);
|
||||
}
|
||||
|
||||
#define mcode_setprot(p, sz, prot) UNUSED(p)
|
||||
|
||||
#endif
|
||||
|
||||
/* -- MCode area protection ----------------------------------------------- */
|
||||
|
||||
/* Define this ONLY if the page protection twiddling becomes a bottleneck. */
|
||||
#ifdef LUAJIT_UNPROTECT_MCODE
|
||||
|
||||
/* It's generally considered to be a potential security risk to have
|
||||
** pages with simultaneous write *and* execute access in a process.
|
||||
**
|
||||
** Do not even think about using this mode for server processes or
|
||||
** apps handling untrusted external data (such as a browser).
|
||||
**
|
||||
** The security risk is not in LuaJIT itself -- but if an adversary finds
|
||||
** any *other* flaw in your C application logic, then any RWX memory page
|
||||
** simplifies writing an exploit considerably.
|
||||
*/
|
||||
#define MCPROT_GEN MCPROT_RWX
|
||||
#define MCPROT_RUN MCPROT_RWX
|
||||
|
||||
static void mcode_protect(jit_State *J, int prot)
|
||||
{
|
||||
UNUSED(J); UNUSED(prot);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* This is the default behaviour and much safer:
|
||||
**
|
||||
** Most of the time the memory pages holding machine code are executable,
|
||||
** but NONE of them is writable.
|
||||
**
|
||||
** The current memory area is marked read-write (but NOT executable) only
|
||||
** during the short time window while the assembler generates machine code.
|
||||
*/
|
||||
#define MCPROT_GEN MCPROT_RW
|
||||
#define MCPROT_RUN MCPROT_RX
|
||||
|
||||
/* Change protection of MCode area. */
|
||||
static void mcode_protect(jit_State *J, int prot)
|
||||
{
|
||||
if (J->mcprot != prot) {
|
||||
mcode_setprot(J->mcarea, J->szmcarea, prot);
|
||||
J->mcprot = prot;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* -- MCode area allocation ----------------------------------------------- */
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
#define mcode_validptr(p) ((p) && (uintptr_t)(p) < (uintptr_t)1<<47)
|
||||
#else
|
||||
#define mcode_validptr(p) ((p) && (uintptr_t)(p) < 0xffff0000)
|
||||
#endif
|
||||
|
||||
#ifdef LJ_TARGET_JUMPRANGE
|
||||
|
||||
/* Get memory within relative jump distance of our code in 64 bit mode. */
|
||||
static void *mcode_alloc(jit_State *J, size_t sz)
|
||||
{
|
||||
/* Target an address in the static assembler code (64K aligned).
|
||||
** Try addresses within a distance of target-range/2+1MB..target+range/2-1MB.
|
||||
*/
|
||||
#if LJ_TARGET_MIPS
|
||||
/* Use the middle of the 256MB-aligned region. */
|
||||
uintptr_t target = ((uintptr_t)(void *)lj_vm_exit_handler & 0xf0000000u) +
|
||||
0x08000000u;
|
||||
#else
|
||||
uintptr_t target = (uintptr_t)(void *)lj_vm_exit_handler & ~(uintptr_t)0xffff;
|
||||
#endif
|
||||
const uintptr_t range = (1u << LJ_TARGET_JUMPRANGE) - (1u << 21);
|
||||
/* First try a contiguous area below the last one. */
|
||||
uintptr_t hint = J->mcarea ? (uintptr_t)J->mcarea - sz : 0;
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) { /* 32 attempts ought to be enough ... */
|
||||
if (mcode_validptr(hint)) {
|
||||
void *p = mcode_alloc_at(J, hint, sz, MCPROT_GEN);
|
||||
|
||||
if (mcode_validptr(p) &&
|
||||
((uintptr_t)p + sz - target < range || target - (uintptr_t)p < range))
|
||||
return p;
|
||||
if (p) mcode_free(J, p, sz); /* Free badly placed area. */
|
||||
}
|
||||
/* Next try probing pseudo-random addresses. */
|
||||
do {
|
||||
hint = (0x78fb ^ LJ_PRNG_BITS(J, 15)) << 16; /* 64K aligned. */
|
||||
} while (!(hint + sz < range));
|
||||
hint = target + hint - (range>>1);
|
||||
}
|
||||
lj_trace_err(J, LJ_TRERR_MCODEAL); /* Give up. OS probably ignores hints? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* All memory addresses are reachable by relative jumps. */
|
||||
#define mcode_alloc(J, sz) mcode_alloc_at((J), 0, (sz), MCPROT_GEN)
|
||||
|
||||
#endif
|
||||
|
||||
/* -- MCode area management ----------------------------------------------- */
|
||||
|
||||
/* Linked list of MCode areas. */
|
||||
typedef struct MCLink {
|
||||
MCode *next; /* Next area. */
|
||||
size_t size; /* Size of current area. */
|
||||
} MCLink;
|
||||
|
||||
/* Allocate a new MCode area. */
|
||||
static void mcode_allocarea(jit_State *J)
|
||||
{
|
||||
MCode *oldarea = J->mcarea;
|
||||
size_t sz = (size_t)J->param[JIT_P_sizemcode] << 10;
|
||||
sz = (sz + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1);
|
||||
J->mcarea = (MCode *)mcode_alloc(J, sz);
|
||||
J->szmcarea = sz;
|
||||
J->mcprot = MCPROT_GEN;
|
||||
J->mctop = (MCode *)((char *)J->mcarea + J->szmcarea);
|
||||
J->mcbot = (MCode *)((char *)J->mcarea + sizeof(MCLink));
|
||||
((MCLink *)J->mcarea)->next = oldarea;
|
||||
((MCLink *)J->mcarea)->size = sz;
|
||||
J->szallmcarea += sz;
|
||||
}
|
||||
|
||||
/* Free all MCode areas. */
|
||||
void lj_mcode_free(jit_State *J)
|
||||
{
|
||||
MCode *mc = J->mcarea;
|
||||
J->mcarea = NULL;
|
||||
J->szallmcarea = 0;
|
||||
while (mc) {
|
||||
MCode *next = ((MCLink *)mc)->next;
|
||||
mcode_free(J, mc, ((MCLink *)mc)->size);
|
||||
mc = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- MCode transactions -------------------------------------------------- */
|
||||
|
||||
/* Reserve the remainder of the current MCode area. */
|
||||
MCode *lj_mcode_reserve(jit_State *J, MCode **lim)
|
||||
{
|
||||
if (!J->mcarea)
|
||||
mcode_allocarea(J);
|
||||
else
|
||||
mcode_protect(J, MCPROT_GEN);
|
||||
*lim = J->mcbot;
|
||||
return J->mctop;
|
||||
}
|
||||
|
||||
/* Commit the top part of the current MCode area. */
|
||||
void lj_mcode_commit(jit_State *J, MCode *top)
|
||||
{
|
||||
J->mctop = top;
|
||||
mcode_protect(J, MCPROT_RUN);
|
||||
}
|
||||
|
||||
/* Abort the reservation. */
|
||||
void lj_mcode_abort(jit_State *J)
|
||||
{
|
||||
mcode_protect(J, MCPROT_RUN);
|
||||
}
|
||||
|
||||
/* Set/reset protection to allow patching of MCode areas. */
|
||||
MCode *lj_mcode_patch(jit_State *J, MCode *ptr, int finish)
|
||||
{
|
||||
#ifdef LUAJIT_UNPROTECT_MCODE
|
||||
UNUSED(J); UNUSED(ptr); UNUSED(finish);
|
||||
return NULL;
|
||||
#else
|
||||
if (finish) {
|
||||
if (J->mcarea == ptr)
|
||||
mcode_protect(J, MCPROT_RUN);
|
||||
else
|
||||
mcode_setprot(ptr, ((MCLink *)ptr)->size, MCPROT_RUN);
|
||||
return NULL;
|
||||
} else {
|
||||
MCode *mc = J->mcarea;
|
||||
/* Try current area first to use the protection cache. */
|
||||
if (ptr >= mc && ptr < (MCode *)((char *)mc + J->szmcarea)) {
|
||||
mcode_protect(J, MCPROT_GEN);
|
||||
return mc;
|
||||
}
|
||||
/* Otherwise search through the list of MCode areas. */
|
||||
for (;;) {
|
||||
mc = ((MCLink *)mc)->next;
|
||||
lua_assert(mc != NULL);
|
||||
if (ptr >= mc && ptr < (MCode *)((char *)mc + ((MCLink *)mc)->size)) {
|
||||
mcode_setprot(mc, ((MCLink *)mc)->size, MCPROT_GEN);
|
||||
return mc;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Limit of MCode reservation reached. */
|
||||
void lj_mcode_limiterr(jit_State *J, size_t need)
|
||||
{
|
||||
size_t sizemcode, maxmcode;
|
||||
lj_mcode_abort(J);
|
||||
sizemcode = (size_t)J->param[JIT_P_sizemcode] << 10;
|
||||
sizemcode = (sizemcode + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1);
|
||||
maxmcode = (size_t)J->param[JIT_P_maxmcode] << 10;
|
||||
if ((size_t)need > sizemcode)
|
||||
lj_trace_err(J, LJ_TRERR_MCODEOV); /* Too long for any area. */
|
||||
if (J->szallmcarea + sizemcode > maxmcode)
|
||||
lj_trace_err(J, LJ_TRERR_MCODEAL);
|
||||
mcode_allocarea(J);
|
||||
lj_trace_err(J, LJ_TRERR_MCODELM); /* Retry with new area. */
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
** Miscellaneous object handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_obj_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
/* Object type names. */
|
||||
LJ_DATADEF const char *const lj_obj_typename[] = { /* ORDER LUA_T */
|
||||
"no value", "nil", "boolean", "userdata", "number", "string",
|
||||
"table", "function", "userdata", "thread", "proto", "cdata"
|
||||
};
|
||||
|
||||
LJ_DATADEF const char *const lj_obj_itypename[] = { /* ORDER LJ_T */
|
||||
"nil", "boolean", "boolean", "userdata", "string", "upval", "thread",
|
||||
"proto", "function", "trace", "cdata", "table", "userdata", "number"
|
||||
};
|
||||
|
||||
/* Compare two objects without calling metamethods. */
|
||||
int lj_obj_equal(cTValue *o1, cTValue *o2)
|
||||
{
|
||||
if (itype(o1) == itype(o2)) {
|
||||
if (tvispri(o1))
|
||||
return 1;
|
||||
if (!tvisnum(o1))
|
||||
return gcrefeq(o1->gcr, o2->gcr);
|
||||
} else if (!tvisnumber(o1) || !tvisnumber(o2)) {
|
||||
return 0;
|
||||
}
|
||||
return numberVnum(o1) == numberVnum(o2);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,860 +0,0 @@
|
||||
/*
|
||||
** Snapshot handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_snap_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASJIT
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#include "lj_bc.h"
|
||||
#include "lj_ir.h"
|
||||
#include "lj_jit.h"
|
||||
#include "lj_iropt.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_snap.h"
|
||||
#include "lj_target.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cdata.h"
|
||||
#endif
|
||||
|
||||
/* Some local macros to save typing. Undef'd at the end. */
|
||||
#define IR(ref) (&J->cur.ir[(ref)])
|
||||
|
||||
/* Pass IR on to next optimization in chain (FOLD). */
|
||||
#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J))
|
||||
|
||||
/* Emit raw IR without passing through optimizations. */
|
||||
#define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J))
|
||||
|
||||
/* -- Snapshot buffer allocation ------------------------------------------ */
|
||||
|
||||
/* Grow snapshot buffer. */
|
||||
void lj_snap_grow_buf_(jit_State *J, MSize need)
|
||||
{
|
||||
MSize maxsnap = (MSize)J->param[JIT_P_maxsnap];
|
||||
if (need > maxsnap)
|
||||
lj_trace_err(J, LJ_TRERR_SNAPOV);
|
||||
lj_mem_growvec(J->L, J->snapbuf, J->sizesnap, maxsnap, SnapShot);
|
||||
J->cur.snap = J->snapbuf;
|
||||
}
|
||||
|
||||
/* Grow snapshot map buffer. */
|
||||
void lj_snap_grow_map_(jit_State *J, MSize need)
|
||||
{
|
||||
if (need < 2*J->sizesnapmap)
|
||||
need = 2*J->sizesnapmap;
|
||||
else if (need < 64)
|
||||
need = 64;
|
||||
J->snapmapbuf = (SnapEntry *)lj_mem_realloc(J->L, J->snapmapbuf,
|
||||
J->sizesnapmap*sizeof(SnapEntry), need*sizeof(SnapEntry));
|
||||
J->cur.snapmap = J->snapmapbuf;
|
||||
J->sizesnapmap = need;
|
||||
}
|
||||
|
||||
/* -- Snapshot generation ------------------------------------------------- */
|
||||
|
||||
/* Add all modified slots to the snapshot. */
|
||||
static MSize snapshot_slots(jit_State *J, SnapEntry *map, BCReg nslots)
|
||||
{
|
||||
IRRef retf = J->chain[IR_RETF]; /* Limits SLOAD restore elimination. */
|
||||
BCReg s;
|
||||
MSize n = 0;
|
||||
for (s = 0; s < nslots; s++) {
|
||||
TRef tr = J->slot[s];
|
||||
IRRef ref = tref_ref(tr);
|
||||
if (ref) {
|
||||
SnapEntry sn = SNAP_TR(s, tr);
|
||||
IRIns *ir = IR(ref);
|
||||
if (!(sn & (SNAP_CONT|SNAP_FRAME)) &&
|
||||
ir->o == IR_SLOAD && ir->op1 == s && ref > retf) {
|
||||
/* No need to snapshot unmodified non-inherited slots. */
|
||||
if (!(ir->op2 & IRSLOAD_INHERIT))
|
||||
continue;
|
||||
/* No need to restore readonly slots and unmodified non-parent slots. */
|
||||
if (!(LJ_DUALNUM && (ir->op2 & IRSLOAD_CONVERT)) &&
|
||||
(ir->op2 & (IRSLOAD_READONLY|IRSLOAD_PARENT)) != IRSLOAD_PARENT)
|
||||
sn |= SNAP_NORESTORE;
|
||||
}
|
||||
if (LJ_SOFTFP && irt_isnum(ir->t))
|
||||
sn |= SNAP_SOFTFPNUM;
|
||||
map[n++] = sn;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Add frame links at the end of the snapshot. */
|
||||
static BCReg snapshot_framelinks(jit_State *J, SnapEntry *map)
|
||||
{
|
||||
cTValue *frame = J->L->base - 1;
|
||||
cTValue *lim = J->L->base - J->baseslot;
|
||||
cTValue *ftop = frame + funcproto(frame_func(frame))->framesize;
|
||||
MSize f = 0;
|
||||
map[f++] = SNAP_MKPC(J->pc); /* The current PC is always the first entry. */
|
||||
while (frame > lim) { /* Backwards traversal of all frames above base. */
|
||||
if (frame_islua(frame)) {
|
||||
map[f++] = SNAP_MKPC(frame_pc(frame));
|
||||
frame = frame_prevl(frame);
|
||||
if (frame + funcproto(frame_func(frame))->framesize > ftop)
|
||||
ftop = frame + funcproto(frame_func(frame))->framesize;
|
||||
} else if (frame_iscont(frame)) {
|
||||
map[f++] = SNAP_MKFTSZ(frame_ftsz(frame));
|
||||
map[f++] = SNAP_MKPC(frame_contpc(frame));
|
||||
frame = frame_prevd(frame);
|
||||
} else {
|
||||
lua_assert(!frame_isc(frame));
|
||||
map[f++] = SNAP_MKFTSZ(frame_ftsz(frame));
|
||||
frame = frame_prevd(frame);
|
||||
}
|
||||
}
|
||||
lua_assert(f == (MSize)(1 + J->framedepth));
|
||||
return (BCReg)(ftop - lim);
|
||||
}
|
||||
|
||||
/* Take a snapshot of the current stack. */
|
||||
static void snapshot_stack(jit_State *J, SnapShot *snap, MSize nsnapmap)
|
||||
{
|
||||
BCReg nslots = J->baseslot + J->maxslot;
|
||||
MSize nent;
|
||||
SnapEntry *p;
|
||||
/* Conservative estimate. */
|
||||
lj_snap_grow_map(J, nsnapmap + nslots + (MSize)J->framedepth+1);
|
||||
p = &J->cur.snapmap[nsnapmap];
|
||||
nent = snapshot_slots(J, p, nslots);
|
||||
snap->topslot = (uint8_t)snapshot_framelinks(J, p + nent);
|
||||
snap->mapofs = (uint16_t)nsnapmap;
|
||||
snap->ref = (IRRef1)J->cur.nins;
|
||||
snap->nent = (uint8_t)nent;
|
||||
snap->nslots = (uint8_t)nslots;
|
||||
snap->count = 0;
|
||||
J->cur.nsnapmap = (uint16_t)(nsnapmap + nent + 1 + J->framedepth);
|
||||
}
|
||||
|
||||
/* Add or merge a snapshot. */
|
||||
void lj_snap_add(jit_State *J)
|
||||
{
|
||||
MSize nsnap = J->cur.nsnap;
|
||||
MSize nsnapmap = J->cur.nsnapmap;
|
||||
/* Merge if no ins. inbetween or if requested and no guard inbetween. */
|
||||
if (J->mergesnap ? !irt_isguard(J->guardemit) :
|
||||
(nsnap > 0 && J->cur.snap[nsnap-1].ref == J->cur.nins)) {
|
||||
if (nsnap == 1) { /* But preserve snap #0 PC. */
|
||||
emitir_raw(IRT(IR_NOP, IRT_NIL), 0, 0);
|
||||
goto nomerge;
|
||||
}
|
||||
nsnapmap = J->cur.snap[--nsnap].mapofs;
|
||||
} else {
|
||||
nomerge:
|
||||
lj_snap_grow_buf(J, nsnap+1);
|
||||
J->cur.nsnap = (uint16_t)(nsnap+1);
|
||||
}
|
||||
J->mergesnap = 0;
|
||||
J->guardemit.irt = 0;
|
||||
snapshot_stack(J, &J->cur.snap[nsnap], nsnapmap);
|
||||
}
|
||||
|
||||
/* -- Snapshot modification ----------------------------------------------- */
|
||||
|
||||
#define SNAP_USEDEF_SLOTS (LJ_MAX_JSLOTS+LJ_STACK_EXTRA)
|
||||
|
||||
/* Find unused slots with reaching-definitions bytecode data-flow analysis. */
|
||||
static BCReg snap_usedef(jit_State *J, uint8_t *udf,
|
||||
const BCIns *pc, BCReg maxslot)
|
||||
{
|
||||
BCReg s;
|
||||
GCobj *o;
|
||||
|
||||
if (maxslot == 0) return 0;
|
||||
#ifdef LUAJIT_USE_VALGRIND
|
||||
/* Avoid errors for harmless reads beyond maxslot. */
|
||||
memset(udf, 1, SNAP_USEDEF_SLOTS);
|
||||
#else
|
||||
memset(udf, 1, maxslot);
|
||||
#endif
|
||||
|
||||
/* Treat open upvalues as used. */
|
||||
o = gcref(J->L->openupval);
|
||||
while (o) {
|
||||
if (uvval(gco2uv(o)) < J->L->base) break;
|
||||
udf[uvval(gco2uv(o)) - J->L->base] = 0;
|
||||
o = gcref(o->gch.nextgc);
|
||||
}
|
||||
|
||||
#define USE_SLOT(s) udf[(s)] &= ~1
|
||||
#define DEF_SLOT(s) udf[(s)] *= 3
|
||||
|
||||
/* Scan through following bytecode and check for uses/defs. */
|
||||
lua_assert(pc >= proto_bc(J->pt) && pc < proto_bc(J->pt) + J->pt->sizebc);
|
||||
for (;;) {
|
||||
BCIns ins = *pc++;
|
||||
BCOp op = bc_op(ins);
|
||||
switch (bcmode_b(op)) {
|
||||
case BCMvar: USE_SLOT(bc_b(ins)); break;
|
||||
default: break;
|
||||
}
|
||||
switch (bcmode_c(op)) {
|
||||
case BCMvar: USE_SLOT(bc_c(ins)); break;
|
||||
case BCMrbase:
|
||||
lua_assert(op == BC_CAT);
|
||||
for (s = bc_b(ins); s <= bc_c(ins); s++) USE_SLOT(s);
|
||||
for (; s < maxslot; s++) DEF_SLOT(s);
|
||||
break;
|
||||
case BCMjump:
|
||||
handle_jump: {
|
||||
BCReg minslot = bc_a(ins);
|
||||
if (op >= BC_FORI && op <= BC_JFORL) minslot += FORL_EXT;
|
||||
else if (op >= BC_ITERL && op <= BC_JITERL) minslot += bc_b(pc[-2])-1;
|
||||
else if (op == BC_UCLO) { pc += bc_j(ins); break; }
|
||||
for (s = minslot; s < maxslot; s++) DEF_SLOT(s);
|
||||
return minslot < maxslot ? minslot : maxslot;
|
||||
}
|
||||
case BCMlit:
|
||||
if (op == BC_JFORL || op == BC_JITERL || op == BC_JLOOP) {
|
||||
goto handle_jump;
|
||||
} else if (bc_isret(op)) {
|
||||
BCReg top = op == BC_RETM ? maxslot : (bc_a(ins) + bc_d(ins)-1);
|
||||
for (s = 0; s < bc_a(ins); s++) DEF_SLOT(s);
|
||||
for (; s < top; s++) USE_SLOT(s);
|
||||
for (; s < maxslot; s++) DEF_SLOT(s);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case BCMfunc: return maxslot; /* NYI: will abort, anyway. */
|
||||
default: break;
|
||||
}
|
||||
switch (bcmode_a(op)) {
|
||||
case BCMvar: USE_SLOT(bc_a(ins)); break;
|
||||
case BCMdst:
|
||||
if (!(op == BC_ISTC || op == BC_ISFC)) DEF_SLOT(bc_a(ins));
|
||||
break;
|
||||
case BCMbase:
|
||||
if (op >= BC_CALLM && op <= BC_VARG) {
|
||||
BCReg top = (op == BC_CALLM || op == BC_CALLMT || bc_c(ins) == 0) ?
|
||||
maxslot : (bc_a(ins) + bc_c(ins));
|
||||
s = bc_a(ins) - ((op == BC_ITERC || op == BC_ITERN) ? 3 : 0);
|
||||
for (; s < top; s++) USE_SLOT(s);
|
||||
for (; s < maxslot; s++) DEF_SLOT(s);
|
||||
if (op == BC_CALLT || op == BC_CALLMT) {
|
||||
for (s = 0; s < bc_a(ins); s++) DEF_SLOT(s);
|
||||
return 0;
|
||||
}
|
||||
} else if (op == BC_KNIL) {
|
||||
for (s = bc_a(ins); s <= bc_d(ins); s++) DEF_SLOT(s);
|
||||
} else if (op == BC_TSETM) {
|
||||
for (s = bc_a(ins)-1; s < maxslot; s++) USE_SLOT(s);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
lua_assert(pc >= proto_bc(J->pt) && pc < proto_bc(J->pt) + J->pt->sizebc);
|
||||
}
|
||||
|
||||
#undef USE_SLOT
|
||||
#undef DEF_SLOT
|
||||
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
/* Purge dead slots before the next snapshot. */
|
||||
void lj_snap_purge(jit_State *J)
|
||||
{
|
||||
uint8_t udf[SNAP_USEDEF_SLOTS];
|
||||
BCReg maxslot = J->maxslot;
|
||||
BCReg s = snap_usedef(J, udf, J->pc, maxslot);
|
||||
for (; s < maxslot; s++)
|
||||
if (udf[s] != 0)
|
||||
J->base[s] = 0; /* Purge dead slots. */
|
||||
}
|
||||
|
||||
/* Shrink last snapshot. */
|
||||
void lj_snap_shrink(jit_State *J)
|
||||
{
|
||||
SnapShot *snap = &J->cur.snap[J->cur.nsnap-1];
|
||||
SnapEntry *map = &J->cur.snapmap[snap->mapofs];
|
||||
MSize n, m, nlim, nent = snap->nent;
|
||||
uint8_t udf[SNAP_USEDEF_SLOTS];
|
||||
BCReg maxslot = J->maxslot;
|
||||
BCReg minslot = snap_usedef(J, udf, snap_pc(map[nent]), maxslot);
|
||||
BCReg baseslot = J->baseslot;
|
||||
maxslot += baseslot;
|
||||
minslot += baseslot;
|
||||
snap->nslots = (uint8_t)maxslot;
|
||||
for (n = m = 0; n < nent; n++) { /* Remove unused slots from snapshot. */
|
||||
BCReg s = snap_slot(map[n]);
|
||||
if (s < minslot || (s < maxslot && udf[s-baseslot] == 0))
|
||||
map[m++] = map[n]; /* Only copy used slots. */
|
||||
}
|
||||
snap->nent = (uint8_t)m;
|
||||
nlim = J->cur.nsnapmap - snap->mapofs - 1;
|
||||
while (n <= nlim) map[m++] = map[n++]; /* Move PC + frame links down. */
|
||||
J->cur.nsnapmap = (uint16_t)(snap->mapofs + m); /* Free up space in map. */
|
||||
}
|
||||
|
||||
/* -- Snapshot access ----------------------------------------------------- */
|
||||
|
||||
/* Initialize a Bloom Filter with all renamed refs.
|
||||
** There are very few renames (often none), so the filter has
|
||||
** very few bits set. This makes it suitable for negative filtering.
|
||||
*/
|
||||
static BloomFilter snap_renamefilter(GCtrace *T, SnapNo lim)
|
||||
{
|
||||
BloomFilter rfilt = 0;
|
||||
IRIns *ir;
|
||||
for (ir = &T->ir[T->nins-1]; ir->o == IR_RENAME; ir--)
|
||||
if (ir->op2 <= lim)
|
||||
bloomset(rfilt, ir->op1);
|
||||
return rfilt;
|
||||
}
|
||||
|
||||
/* Process matching renames to find the original RegSP. */
|
||||
static RegSP snap_renameref(GCtrace *T, SnapNo lim, IRRef ref, RegSP rs)
|
||||
{
|
||||
IRIns *ir;
|
||||
for (ir = &T->ir[T->nins-1]; ir->o == IR_RENAME; ir--)
|
||||
if (ir->op1 == ref && ir->op2 <= lim)
|
||||
rs = ir->prev;
|
||||
return rs;
|
||||
}
|
||||
|
||||
/* Copy RegSP from parent snapshot to the parent links of the IR. */
|
||||
IRIns *lj_snap_regspmap(GCtrace *T, SnapNo snapno, IRIns *ir)
|
||||
{
|
||||
SnapShot *snap = &T->snap[snapno];
|
||||
SnapEntry *map = &T->snapmap[snap->mapofs];
|
||||
BloomFilter rfilt = snap_renamefilter(T, snapno);
|
||||
MSize n = 0;
|
||||
IRRef ref = 0;
|
||||
for ( ; ; ir++) {
|
||||
uint32_t rs;
|
||||
if (ir->o == IR_SLOAD) {
|
||||
if (!(ir->op2 & IRSLOAD_PARENT)) break;
|
||||
for ( ; ; n++) {
|
||||
lua_assert(n < snap->nent);
|
||||
if (snap_slot(map[n]) == ir->op1) {
|
||||
ref = snap_ref(map[n++]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (LJ_SOFTFP && ir->o == IR_HIOP) {
|
||||
ref++;
|
||||
} else if (ir->o == IR_PVAL) {
|
||||
ref = ir->op1 + REF_BIAS;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
rs = T->ir[ref].prev;
|
||||
if (bloomtest(rfilt, ref))
|
||||
rs = snap_renameref(T, snapno, ref, rs);
|
||||
ir->prev = (uint16_t)rs;
|
||||
lua_assert(regsp_used(rs));
|
||||
}
|
||||
return ir;
|
||||
}
|
||||
|
||||
/* -- Snapshot replay ----------------------------------------------------- */
|
||||
|
||||
/* Replay constant from parent trace. */
|
||||
static TRef snap_replay_const(jit_State *J, IRIns *ir)
|
||||
{
|
||||
/* Only have to deal with constants that can occur in stack slots. */
|
||||
switch ((IROp)ir->o) {
|
||||
case IR_KPRI: return TREF_PRI(irt_type(ir->t));
|
||||
case IR_KINT: return lj_ir_kint(J, ir->i);
|
||||
case IR_KGC: return lj_ir_kgc(J, ir_kgc(ir), irt_t(ir->t));
|
||||
case IR_KNUM: return lj_ir_k64(J, IR_KNUM, ir_knum(ir));
|
||||
case IR_KINT64: return lj_ir_k64(J, IR_KINT64, ir_kint64(ir));
|
||||
case IR_KPTR: return lj_ir_kptr(J, ir_kptr(ir)); /* Continuation. */
|
||||
default: lua_assert(0); return TREF_NIL; break;
|
||||
}
|
||||
}
|
||||
|
||||
/* De-duplicate parent reference. */
|
||||
static TRef snap_dedup(jit_State *J, SnapEntry *map, MSize nmax, IRRef ref)
|
||||
{
|
||||
MSize j;
|
||||
for (j = 0; j < nmax; j++)
|
||||
if (snap_ref(map[j]) == ref)
|
||||
return J->slot[snap_slot(map[j])] & ~(SNAP_CONT|SNAP_FRAME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Emit parent reference with de-duplication. */
|
||||
static TRef snap_pref(jit_State *J, GCtrace *T, SnapEntry *map, MSize nmax,
|
||||
BloomFilter seen, IRRef ref)
|
||||
{
|
||||
IRIns *ir = &T->ir[ref];
|
||||
TRef tr;
|
||||
if (irref_isk(ref))
|
||||
tr = snap_replay_const(J, ir);
|
||||
else if (!regsp_used(ir->prev))
|
||||
tr = 0;
|
||||
else if (!bloomtest(seen, ref) || (tr = snap_dedup(J, map, nmax, ref)) == 0)
|
||||
tr = emitir(IRT(IR_PVAL, irt_type(ir->t)), ref - REF_BIAS, 0);
|
||||
return tr;
|
||||
}
|
||||
|
||||
/* Check whether a sunk store corresponds to an allocation. Slow path. */
|
||||
static int snap_sunk_store2(jit_State *J, IRIns *ira, IRIns *irs)
|
||||
{
|
||||
if (irs->o == IR_ASTORE || irs->o == IR_HSTORE ||
|
||||
irs->o == IR_FSTORE || irs->o == IR_XSTORE) {
|
||||
IRIns *irk = IR(irs->op1);
|
||||
if (irk->o == IR_AREF || irk->o == IR_HREFK)
|
||||
irk = IR(irk->op1);
|
||||
return (IR(irk->op1) == ira);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether a sunk store corresponds to an allocation. Fast path. */
|
||||
static LJ_AINLINE int snap_sunk_store(jit_State *J, IRIns *ira, IRIns *irs)
|
||||
{
|
||||
if (irs->s != 255)
|
||||
return (ira + irs->s == irs); /* Fast check. */
|
||||
return snap_sunk_store2(J, ira, irs);
|
||||
}
|
||||
|
||||
/* Replay snapshot state to setup side trace. */
|
||||
void lj_snap_replay(jit_State *J, GCtrace *T)
|
||||
{
|
||||
SnapShot *snap = &T->snap[J->exitno];
|
||||
SnapEntry *map = &T->snapmap[snap->mapofs];
|
||||
MSize n, nent = snap->nent;
|
||||
BloomFilter seen = 0;
|
||||
int pass23 = 0;
|
||||
J->framedepth = 0;
|
||||
/* Emit IR for slots inherited from parent snapshot. */
|
||||
for (n = 0; n < nent; n++) {
|
||||
SnapEntry sn = map[n];
|
||||
BCReg s = snap_slot(sn);
|
||||
IRRef ref = snap_ref(sn);
|
||||
IRIns *ir = &T->ir[ref];
|
||||
TRef tr;
|
||||
/* The bloom filter avoids O(nent^2) overhead for de-duping slots. */
|
||||
if (bloomtest(seen, ref) && (tr = snap_dedup(J, map, n, ref)) != 0)
|
||||
goto setslot;
|
||||
bloomset(seen, ref);
|
||||
if (irref_isk(ref)) {
|
||||
tr = snap_replay_const(J, ir);
|
||||
} else if (!regsp_used(ir->prev)) {
|
||||
pass23 = 1;
|
||||
lua_assert(s != 0);
|
||||
tr = s;
|
||||
} else {
|
||||
IRType t = irt_type(ir->t);
|
||||
uint32_t mode = IRSLOAD_INHERIT|IRSLOAD_PARENT;
|
||||
if (LJ_SOFTFP && (sn & SNAP_SOFTFPNUM)) t = IRT_NUM;
|
||||
if (ir->o == IR_SLOAD) mode |= (ir->op2 & IRSLOAD_READONLY);
|
||||
tr = emitir_raw(IRT(IR_SLOAD, t), s, mode);
|
||||
}
|
||||
setslot:
|
||||
J->slot[s] = tr | (sn&(SNAP_CONT|SNAP_FRAME)); /* Same as TREF_* flags. */
|
||||
J->framedepth += ((sn & (SNAP_CONT|SNAP_FRAME)) && s);
|
||||
if ((sn & SNAP_FRAME))
|
||||
J->baseslot = s+1;
|
||||
}
|
||||
if (pass23) {
|
||||
IRIns *irlast = &T->ir[snap->ref];
|
||||
pass23 = 0;
|
||||
/* Emit dependent PVALs. */
|
||||
for (n = 0; n < nent; n++) {
|
||||
SnapEntry sn = map[n];
|
||||
IRRef refp = snap_ref(sn);
|
||||
IRIns *ir = &T->ir[refp];
|
||||
if (regsp_reg(ir->r) == RID_SUNK) {
|
||||
if (J->slot[snap_slot(sn)] != snap_slot(sn)) continue;
|
||||
pass23 = 1;
|
||||
lua_assert(ir->o == IR_TNEW || ir->o == IR_TDUP ||
|
||||
ir->o == IR_CNEW || ir->o == IR_CNEWI);
|
||||
if (ir->op1 >= T->nk) snap_pref(J, T, map, nent, seen, ir->op1);
|
||||
if (ir->op2 >= T->nk) snap_pref(J, T, map, nent, seen, ir->op2);
|
||||
if (LJ_HASFFI && ir->o == IR_CNEWI) {
|
||||
if (LJ_32 && refp+1 < T->nins && (ir+1)->o == IR_HIOP)
|
||||
snap_pref(J, T, map, nent, seen, (ir+1)->op2);
|
||||
} else {
|
||||
IRIns *irs;
|
||||
for (irs = ir+1; irs < irlast; irs++)
|
||||
if (irs->r == RID_SINK && snap_sunk_store(J, ir, irs)) {
|
||||
if (snap_pref(J, T, map, nent, seen, irs->op2) == 0)
|
||||
snap_pref(J, T, map, nent, seen, T->ir[irs->op2].op1);
|
||||
else if ((LJ_SOFTFP || (LJ_32 && LJ_HASFFI)) &&
|
||||
irs+1 < irlast && (irs+1)->o == IR_HIOP)
|
||||
snap_pref(J, T, map, nent, seen, (irs+1)->op2);
|
||||
}
|
||||
}
|
||||
} else if (!irref_isk(refp) && !regsp_used(ir->prev)) {
|
||||
lua_assert(ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT);
|
||||
J->slot[snap_slot(sn)] = snap_pref(J, T, map, nent, seen, ir->op1);
|
||||
}
|
||||
}
|
||||
/* Replay sunk instructions. */
|
||||
for (n = 0; pass23 && n < nent; n++) {
|
||||
SnapEntry sn = map[n];
|
||||
IRRef refp = snap_ref(sn);
|
||||
IRIns *ir = &T->ir[refp];
|
||||
if (regsp_reg(ir->r) == RID_SUNK) {
|
||||
TRef op1, op2;
|
||||
if (J->slot[snap_slot(sn)] != snap_slot(sn)) { /* De-dup allocs. */
|
||||
J->slot[snap_slot(sn)] = J->slot[J->slot[snap_slot(sn)]];
|
||||
continue;
|
||||
}
|
||||
op1 = ir->op1;
|
||||
if (op1 >= T->nk) op1 = snap_pref(J, T, map, nent, seen, op1);
|
||||
op2 = ir->op2;
|
||||
if (op2 >= T->nk) op2 = snap_pref(J, T, map, nent, seen, op2);
|
||||
if (LJ_HASFFI && ir->o == IR_CNEWI) {
|
||||
if (LJ_32 && refp+1 < T->nins && (ir+1)->o == IR_HIOP) {
|
||||
lj_needsplit(J); /* Emit joining HIOP. */
|
||||
op2 = emitir_raw(IRT(IR_HIOP, IRT_I64), op2,
|
||||
snap_pref(J, T, map, nent, seen, (ir+1)->op2));
|
||||
}
|
||||
J->slot[snap_slot(sn)] = emitir(ir->ot, op1, op2);
|
||||
} else {
|
||||
IRIns *irs;
|
||||
TRef tr = emitir(ir->ot, op1, op2);
|
||||
J->slot[snap_slot(sn)] = tr;
|
||||
for (irs = ir+1; irs < irlast; irs++)
|
||||
if (irs->r == RID_SINK && snap_sunk_store(J, ir, irs)) {
|
||||
IRIns *irr = &T->ir[irs->op1];
|
||||
TRef val, key = irr->op2, tmp = tr;
|
||||
if (irr->o != IR_FREF) {
|
||||
IRIns *irk = &T->ir[key];
|
||||
if (irr->o == IR_HREFK)
|
||||
key = lj_ir_kslot(J, snap_replay_const(J, &T->ir[irk->op1]),
|
||||
irk->op2);
|
||||
else
|
||||
key = snap_replay_const(J, irk);
|
||||
if (irr->o == IR_HREFK || irr->o == IR_AREF) {
|
||||
IRIns *irf = &T->ir[irr->op1];
|
||||
tmp = emitir(irf->ot, tmp, irf->op2);
|
||||
}
|
||||
}
|
||||
tmp = emitir(irr->ot, tmp, key);
|
||||
val = snap_pref(J, T, map, nent, seen, irs->op2);
|
||||
if (val == 0) {
|
||||
IRIns *irc = &T->ir[irs->op2];
|
||||
lua_assert(irc->o == IR_CONV && irc->op2 == IRCONV_NUM_INT);
|
||||
val = snap_pref(J, T, map, nent, seen, irc->op1);
|
||||
val = emitir(IRTN(IR_CONV), val, IRCONV_NUM_INT);
|
||||
} else if ((LJ_SOFTFP || (LJ_32 && LJ_HASFFI)) &&
|
||||
irs+1 < irlast && (irs+1)->o == IR_HIOP) {
|
||||
IRType t = IRT_I64;
|
||||
if (LJ_SOFTFP && irt_type((irs+1)->t) == IRT_SOFTFP)
|
||||
t = IRT_NUM;
|
||||
lj_needsplit(J);
|
||||
if (irref_isk(irs->op2) && irref_isk((irs+1)->op2)) {
|
||||
uint64_t k = (uint32_t)T->ir[irs->op2].i +
|
||||
((uint64_t)T->ir[(irs+1)->op2].i << 32);
|
||||
val = lj_ir_k64(J, t == IRT_I64 ? IR_KINT64 : IR_KNUM,
|
||||
lj_ir_k64_find(J, k));
|
||||
} else {
|
||||
val = emitir_raw(IRT(IR_HIOP, t), val,
|
||||
snap_pref(J, T, map, nent, seen, (irs+1)->op2));
|
||||
}
|
||||
tmp = emitir(IRT(irs->o, t), tmp, val);
|
||||
continue;
|
||||
}
|
||||
tmp = emitir(irs->ot, tmp, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
J->base = J->slot + J->baseslot;
|
||||
J->maxslot = snap->nslots - J->baseslot;
|
||||
lj_snap_add(J);
|
||||
if (pass23) /* Need explicit GC step _after_ initial snapshot. */
|
||||
emitir_raw(IRTG(IR_GCSTEP, IRT_NIL), 0, 0);
|
||||
}
|
||||
|
||||
/* -- Snapshot restore ---------------------------------------------------- */
|
||||
|
||||
static void snap_unsink(jit_State *J, GCtrace *T, ExitState *ex,
|
||||
SnapNo snapno, BloomFilter rfilt,
|
||||
IRIns *ir, TValue *o);
|
||||
|
||||
/* Restore a value from the trace exit state. */
|
||||
static void snap_restoreval(jit_State *J, GCtrace *T, ExitState *ex,
|
||||
SnapNo snapno, BloomFilter rfilt,
|
||||
IRRef ref, TValue *o)
|
||||
{
|
||||
IRIns *ir = &T->ir[ref];
|
||||
IRType1 t = ir->t;
|
||||
RegSP rs = ir->prev;
|
||||
if (irref_isk(ref)) { /* Restore constant slot. */
|
||||
lj_ir_kvalue(J->L, o, ir);
|
||||
return;
|
||||
}
|
||||
if (LJ_UNLIKELY(bloomtest(rfilt, ref)))
|
||||
rs = snap_renameref(T, snapno, ref, rs);
|
||||
if (ra_hasspill(regsp_spill(rs))) { /* Restore from spill slot. */
|
||||
int32_t *sps = &ex->spill[regsp_spill(rs)];
|
||||
if (irt_isinteger(t)) {
|
||||
setintV(o, *sps);
|
||||
#if !LJ_SOFTFP
|
||||
} else if (irt_isnum(t)) {
|
||||
o->u64 = *(uint64_t *)sps;
|
||||
#endif
|
||||
} else if (LJ_64 && irt_islightud(t)) {
|
||||
/* 64 bit lightuserdata which may escape already has the tag bits. */
|
||||
o->u64 = *(uint64_t *)sps;
|
||||
} else {
|
||||
lua_assert(!irt_ispri(t)); /* PRI refs never have a spill slot. */
|
||||
setgcrefi(o->gcr, *sps);
|
||||
setitype(o, irt_toitype(t));
|
||||
}
|
||||
} else { /* Restore from register. */
|
||||
Reg r = regsp_reg(rs);
|
||||
if (ra_noreg(r)) {
|
||||
lua_assert(ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT);
|
||||
snap_restoreval(J, T, ex, snapno, rfilt, ir->op1, o);
|
||||
if (LJ_DUALNUM) setnumV(o, (lua_Number)intV(o));
|
||||
return;
|
||||
} else if (irt_isinteger(t)) {
|
||||
setintV(o, (int32_t)ex->gpr[r-RID_MIN_GPR]);
|
||||
#if !LJ_SOFTFP
|
||||
} else if (irt_isnum(t)) {
|
||||
setnumV(o, ex->fpr[r-RID_MIN_FPR]);
|
||||
#endif
|
||||
} else if (LJ_64 && irt_islightud(t)) {
|
||||
/* 64 bit lightuserdata which may escape already has the tag bits. */
|
||||
o->u64 = ex->gpr[r-RID_MIN_GPR];
|
||||
} else {
|
||||
if (!irt_ispri(t))
|
||||
setgcrefi(o->gcr, ex->gpr[r-RID_MIN_GPR]);
|
||||
setitype(o, irt_toitype(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if LJ_HASFFI
|
||||
/* Restore raw data from the trace exit state. */
|
||||
static void snap_restoredata(GCtrace *T, ExitState *ex,
|
||||
SnapNo snapno, BloomFilter rfilt,
|
||||
IRRef ref, void *dst, CTSize sz)
|
||||
{
|
||||
IRIns *ir = &T->ir[ref];
|
||||
RegSP rs = ir->prev;
|
||||
int32_t *src;
|
||||
uint64_t tmp;
|
||||
if (irref_isk(ref)) {
|
||||
if (ir->o == IR_KNUM || ir->o == IR_KINT64) {
|
||||
src = mref(ir->ptr, int32_t);
|
||||
} else if (sz == 8) {
|
||||
tmp = (uint64_t)(uint32_t)ir->i;
|
||||
src = (int32_t *)&tmp;
|
||||
} else {
|
||||
src = &ir->i;
|
||||
}
|
||||
} else {
|
||||
if (LJ_UNLIKELY(bloomtest(rfilt, ref)))
|
||||
rs = snap_renameref(T, snapno, ref, rs);
|
||||
if (ra_hasspill(regsp_spill(rs))) {
|
||||
src = &ex->spill[regsp_spill(rs)];
|
||||
if (sz == 8 && !irt_is64(ir->t)) {
|
||||
tmp = (uint64_t)(uint32_t)*src;
|
||||
src = (int32_t *)&tmp;
|
||||
}
|
||||
} else {
|
||||
Reg r = regsp_reg(rs);
|
||||
if (ra_noreg(r)) {
|
||||
/* Note: this assumes CNEWI is never used for SOFTFP split numbers. */
|
||||
lua_assert(sz == 8 && ir->o == IR_CONV && ir->op2 == IRCONV_NUM_INT);
|
||||
snap_restoredata(T, ex, snapno, rfilt, ir->op1, dst, 4);
|
||||
*(lua_Number *)dst = (lua_Number)*(int32_t *)dst;
|
||||
return;
|
||||
}
|
||||
src = (int32_t *)&ex->gpr[r-RID_MIN_GPR];
|
||||
#if !LJ_SOFTFP
|
||||
if (r >= RID_MAX_GPR) {
|
||||
src = (int32_t *)&ex->fpr[r-RID_MIN_FPR];
|
||||
#if LJ_TARGET_PPC
|
||||
if (sz == 4) { /* PPC FPRs are always doubles. */
|
||||
*(float *)dst = (float)*(double *)src;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (LJ_BE && sz == 4) src++;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
lua_assert(sz == 1 || sz == 2 || sz == 4 || sz == 8);
|
||||
if (sz == 4) *(int32_t *)dst = *src;
|
||||
else if (sz == 8) *(int64_t *)dst = *(int64_t *)src;
|
||||
else if (sz == 1) *(int8_t *)dst = (int8_t)*src;
|
||||
else *(int16_t *)dst = (int16_t)*src;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Unsink allocation from the trace exit state. Unsink sunk stores. */
|
||||
static void snap_unsink(jit_State *J, GCtrace *T, ExitState *ex,
|
||||
SnapNo snapno, BloomFilter rfilt,
|
||||
IRIns *ir, TValue *o)
|
||||
{
|
||||
lua_assert(ir->o == IR_TNEW || ir->o == IR_TDUP ||
|
||||
ir->o == IR_CNEW || ir->o == IR_CNEWI);
|
||||
#if LJ_HASFFI
|
||||
if (ir->o == IR_CNEW || ir->o == IR_CNEWI) {
|
||||
CTState *cts = ctype_ctsG(J2G(J));
|
||||
CTypeID id = (CTypeID)T->ir[ir->op1].i;
|
||||
CTSize sz = lj_ctype_size(cts, id);
|
||||
GCcdata *cd = lj_cdata_new(cts, id, sz);
|
||||
setcdataV(J->L, o, cd);
|
||||
if (ir->o == IR_CNEWI) {
|
||||
uint8_t *p = (uint8_t *)cdataptr(cd);
|
||||
lua_assert(sz == 4 || sz == 8);
|
||||
if (LJ_32 && sz == 8 && ir+1 < T->ir + T->nins && (ir+1)->o == IR_HIOP) {
|
||||
snap_restoredata(T, ex, snapno, rfilt, (ir+1)->op2, LJ_LE?p+4:p, 4);
|
||||
if (LJ_BE) p += 4;
|
||||
sz = 4;
|
||||
}
|
||||
snap_restoredata(T, ex, snapno, rfilt, ir->op2, p, sz);
|
||||
} else {
|
||||
IRIns *irs, *irlast = &T->ir[T->snap[snapno].ref];
|
||||
for (irs = ir+1; irs < irlast; irs++)
|
||||
if (irs->r == RID_SINK && snap_sunk_store(J, ir, irs)) {
|
||||
IRIns *iro = &T->ir[T->ir[irs->op1].op2];
|
||||
uint8_t *p = (uint8_t *)cd;
|
||||
CTSize szs;
|
||||
lua_assert(irs->o == IR_XSTORE && T->ir[irs->op1].o == IR_ADD);
|
||||
lua_assert(iro->o == IR_KINT || iro->o == IR_KINT64);
|
||||
if (irt_is64(irs->t)) szs = 8;
|
||||
else if (irt_isi8(irs->t) || irt_isu8(irs->t)) szs = 1;
|
||||
else if (irt_isi16(irs->t) || irt_isu16(irs->t)) szs = 2;
|
||||
else szs = 4;
|
||||
if (LJ_64 && iro->o == IR_KINT64)
|
||||
p += (int64_t)ir_k64(iro)->u64;
|
||||
else
|
||||
p += iro->i;
|
||||
lua_assert(p >= (uint8_t *)cdataptr(cd) &&
|
||||
p + szs <= (uint8_t *)cdataptr(cd) + sz);
|
||||
if (LJ_32 && irs+1 < T->ir + T->nins && (irs+1)->o == IR_HIOP) {
|
||||
lua_assert(szs == 4);
|
||||
snap_restoredata(T, ex, snapno, rfilt, (irs+1)->op2, LJ_LE?p+4:p,4);
|
||||
if (LJ_BE) p += 4;
|
||||
}
|
||||
snap_restoredata(T, ex, snapno, rfilt, irs->op2, p, szs);
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
IRIns *irs, *irlast;
|
||||
GCtab *t = ir->o == IR_TNEW ? lj_tab_new(J->L, ir->op1, ir->op2) :
|
||||
lj_tab_dup(J->L, ir_ktab(&T->ir[ir->op1]));
|
||||
settabV(J->L, o, t);
|
||||
irlast = &T->ir[T->snap[snapno].ref];
|
||||
for (irs = ir+1; irs < irlast; irs++)
|
||||
if (irs->r == RID_SINK && snap_sunk_store(J, ir, irs)) {
|
||||
IRIns *irk = &T->ir[irs->op1];
|
||||
TValue tmp, *val;
|
||||
lua_assert(irs->o == IR_ASTORE || irs->o == IR_HSTORE ||
|
||||
irs->o == IR_FSTORE);
|
||||
if (irk->o == IR_FREF) {
|
||||
lua_assert(irk->op2 == IRFL_TAB_META);
|
||||
snap_restoreval(J, T, ex, snapno, rfilt, irs->op2, &tmp);
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
setgcref(t->metatable, obj2gco(tabV(&tmp)));
|
||||
} else {
|
||||
irk = &T->ir[irk->op2];
|
||||
if (irk->o == IR_KSLOT) irk = &T->ir[irk->op1];
|
||||
lj_ir_kvalue(J->L, &tmp, irk);
|
||||
val = lj_tab_set(J->L, t, &tmp);
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
snap_restoreval(J, T, ex, snapno, rfilt, irs->op2, val);
|
||||
if (LJ_SOFTFP && irs+1 < T->ir + T->nins && (irs+1)->o == IR_HIOP) {
|
||||
snap_restoreval(J, T, ex, snapno, rfilt, (irs+1)->op2, &tmp);
|
||||
val->u32.hi = tmp.u32.lo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore interpreter state from exit state with the help of a snapshot. */
|
||||
const BCIns *lj_snap_restore(jit_State *J, void *exptr)
|
||||
{
|
||||
ExitState *ex = (ExitState *)exptr;
|
||||
SnapNo snapno = J->exitno; /* For now, snapno == exitno. */
|
||||
GCtrace *T = traceref(J, J->parent);
|
||||
SnapShot *snap = &T->snap[snapno];
|
||||
MSize n, nent = snap->nent;
|
||||
SnapEntry *map = &T->snapmap[snap->mapofs];
|
||||
SnapEntry *flinks = &T->snapmap[snap_nextofs(T, snap)-1];
|
||||
int32_t ftsz0;
|
||||
TValue *frame;
|
||||
BloomFilter rfilt = snap_renamefilter(T, snapno);
|
||||
const BCIns *pc = snap_pc(map[nent]);
|
||||
lua_State *L = J->L;
|
||||
|
||||
/* Set interpreter PC to the next PC to get correct error messages. */
|
||||
setcframe_pc(cframe_raw(L->cframe), pc+1);
|
||||
|
||||
/* Make sure the stack is big enough for the slots from the snapshot. */
|
||||
if (LJ_UNLIKELY(L->base + snap->topslot >= tvref(L->maxstack))) {
|
||||
L->top = curr_topL(L);
|
||||
lj_state_growstack(L, snap->topslot - curr_proto(L)->framesize);
|
||||
}
|
||||
|
||||
/* Fill stack slots with data from the registers and spill slots. */
|
||||
frame = L->base-1;
|
||||
ftsz0 = frame_ftsz(frame); /* Preserve link to previous frame in slot #0. */
|
||||
for (n = 0; n < nent; n++) {
|
||||
SnapEntry sn = map[n];
|
||||
if (!(sn & SNAP_NORESTORE)) {
|
||||
TValue *o = &frame[snap_slot(sn)];
|
||||
IRRef ref = snap_ref(sn);
|
||||
IRIns *ir = &T->ir[ref];
|
||||
if (ir->r == RID_SUNK) {
|
||||
MSize j;
|
||||
for (j = 0; j < n; j++)
|
||||
if (snap_ref(map[j]) == ref) { /* De-duplicate sunk allocations. */
|
||||
copyTV(L, o, &frame[snap_slot(map[j])]);
|
||||
goto dupslot;
|
||||
}
|
||||
snap_unsink(J, T, ex, snapno, rfilt, ir, o);
|
||||
dupslot:
|
||||
continue;
|
||||
}
|
||||
snap_restoreval(J, T, ex, snapno, rfilt, ref, o);
|
||||
if (LJ_SOFTFP && (sn & SNAP_SOFTFPNUM) && tvisint(o)) {
|
||||
TValue tmp;
|
||||
snap_restoreval(J, T, ex, snapno, rfilt, ref+1, &tmp);
|
||||
o->u32.hi = tmp.u32.lo;
|
||||
} else if ((sn & (SNAP_CONT|SNAP_FRAME))) {
|
||||
/* Overwrite tag with frame link. */
|
||||
o->fr.tp.ftsz = snap_slot(sn) != 0 ? (int32_t)*flinks-- : ftsz0;
|
||||
L->base = o+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_assert(map + nent == flinks);
|
||||
|
||||
/* Compute current stack top. */
|
||||
switch (bc_op(*pc)) {
|
||||
case BC_CALLM: case BC_CALLMT: case BC_RETM: case BC_TSETM:
|
||||
L->top = frame + snap->nslots;
|
||||
break;
|
||||
default:
|
||||
L->top = curr_topL(L);
|
||||
break;
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
#undef IR
|
||||
#undef emitir_raw
|
||||
#undef emitir
|
||||
|
||||
#endif
|
||||
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
** State and stack handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lj_state_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_func.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#endif
|
||||
#include "lj_trace.h"
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_vm.h"
|
||||
#include "lj_lex.h"
|
||||
#include "lj_alloc.h"
|
||||
|
||||
/* -- Stack handling ------------------------------------------------------ */
|
||||
|
||||
/* Stack sizes. */
|
||||
#define LJ_STACK_MIN LUA_MINSTACK /* Min. stack size. */
|
||||
#define LJ_STACK_MAX LUAI_MAXSTACK /* Max. stack size. */
|
||||
#define LJ_STACK_START (2*LJ_STACK_MIN) /* Starting stack size. */
|
||||
#define LJ_STACK_MAXEX (LJ_STACK_MAX + 1 + LJ_STACK_EXTRA)
|
||||
|
||||
/* Explanation of LJ_STACK_EXTRA:
|
||||
**
|
||||
** Calls to metamethods store their arguments beyond the current top
|
||||
** without checking for the stack limit. This avoids stack resizes which
|
||||
** would invalidate passed TValue pointers. The stack check is performed
|
||||
** later by the function header. This can safely resize the stack or raise
|
||||
** an error. Thus we need some extra slots beyond the current stack limit.
|
||||
**
|
||||
** Most metamethods need 4 slots above top (cont, mobj, arg1, arg2) plus
|
||||
** one extra slot if mobj is not a function. Only lj_meta_tset needs 5
|
||||
** slots above top, but then mobj is always a function. So we can get by
|
||||
** with 5 extra slots.
|
||||
*/
|
||||
|
||||
/* Resize stack slots and adjust pointers in state. */
|
||||
static void resizestack(lua_State *L, MSize n)
|
||||
{
|
||||
TValue *st, *oldst = tvref(L->stack);
|
||||
ptrdiff_t delta;
|
||||
MSize oldsize = L->stacksize;
|
||||
MSize realsize = n + 1 + LJ_STACK_EXTRA;
|
||||
GCobj *up;
|
||||
lua_assert((MSize)(tvref(L->maxstack)-oldst)==L->stacksize-LJ_STACK_EXTRA-1);
|
||||
st = (TValue *)lj_mem_realloc(L, tvref(L->stack),
|
||||
(MSize)(L->stacksize*sizeof(TValue)),
|
||||
(MSize)(realsize*sizeof(TValue)));
|
||||
setmref(L->stack, st);
|
||||
delta = (char *)st - (char *)oldst;
|
||||
setmref(L->maxstack, st + n);
|
||||
while (oldsize < realsize) /* Clear new slots. */
|
||||
setnilV(st + oldsize++);
|
||||
L->stacksize = realsize;
|
||||
L->base = (TValue *)((char *)L->base + delta);
|
||||
L->top = (TValue *)((char *)L->top + delta);
|
||||
for (up = gcref(L->openupval); up != NULL; up = gcnext(up))
|
||||
setmref(gco2uv(up)->v, (TValue *)((char *)uvval(gco2uv(up)) + delta));
|
||||
if (obj2gco(L) == gcref(G(L)->jit_L))
|
||||
setmref(G(L)->jit_base, mref(G(L)->jit_base, char) + delta);
|
||||
}
|
||||
|
||||
/* Relimit stack after error, in case the limit was overdrawn. */
|
||||
void lj_state_relimitstack(lua_State *L)
|
||||
{
|
||||
if (L->stacksize > LJ_STACK_MAXEX && L->top-tvref(L->stack) < LJ_STACK_MAX-1)
|
||||
resizestack(L, LJ_STACK_MAX);
|
||||
}
|
||||
|
||||
/* Try to shrink the stack (called from GC). */
|
||||
void lj_state_shrinkstack(lua_State *L, MSize used)
|
||||
{
|
||||
if (L->stacksize > LJ_STACK_MAXEX)
|
||||
return; /* Avoid stack shrinking while handling stack overflow. */
|
||||
if (4*used < L->stacksize &&
|
||||
2*(LJ_STACK_START+LJ_STACK_EXTRA) < L->stacksize &&
|
||||
obj2gco(L) != gcref(G(L)->jit_L)) /* Don't shrink stack of live trace. */
|
||||
resizestack(L, L->stacksize >> 1);
|
||||
}
|
||||
|
||||
/* Try to grow stack. */
|
||||
void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need)
|
||||
{
|
||||
MSize n;
|
||||
if (L->stacksize > LJ_STACK_MAXEX) /* Overflow while handling overflow? */
|
||||
lj_err_throw(L, LUA_ERRERR);
|
||||
n = L->stacksize + need;
|
||||
if (n > LJ_STACK_MAX) {
|
||||
n += 2*LUA_MINSTACK;
|
||||
} else if (n < 2*L->stacksize) {
|
||||
n = 2*L->stacksize;
|
||||
if (n >= LJ_STACK_MAX)
|
||||
n = LJ_STACK_MAX;
|
||||
}
|
||||
resizestack(L, n);
|
||||
if (L->stacksize > LJ_STACK_MAXEX)
|
||||
lj_err_msg(L, LJ_ERR_STKOV);
|
||||
}
|
||||
|
||||
void LJ_FASTCALL lj_state_growstack1(lua_State *L)
|
||||
{
|
||||
lj_state_growstack(L, 1);
|
||||
}
|
||||
|
||||
/* Allocate basic stack for new state. */
|
||||
static void stack_init(lua_State *L1, lua_State *L)
|
||||
{
|
||||
TValue *stend, *st = lj_mem_newvec(L, LJ_STACK_START+LJ_STACK_EXTRA, TValue);
|
||||
setmref(L1->stack, st);
|
||||
L1->stacksize = LJ_STACK_START + LJ_STACK_EXTRA;
|
||||
stend = st + L1->stacksize;
|
||||
setmref(L1->maxstack, stend - LJ_STACK_EXTRA - 1);
|
||||
L1->base = L1->top = st+1;
|
||||
setthreadV(L1, st, L1); /* Needed for curr_funcisL() on empty stack. */
|
||||
while (st < stend) /* Clear new slots. */
|
||||
setnilV(st++);
|
||||
}
|
||||
|
||||
/* -- State handling ------------------------------------------------------ */
|
||||
|
||||
/* Open parts that may cause memory-allocation errors. */
|
||||
static TValue *cpluaopen(lua_State *L, lua_CFunction dummy, void *ud)
|
||||
{
|
||||
global_State *g = G(L);
|
||||
UNUSED(dummy);
|
||||
UNUSED(ud);
|
||||
stack_init(L, L);
|
||||
/* NOBARRIER: State initialization, all objects are white. */
|
||||
setgcref(L->env, obj2gco(lj_tab_new(L, 0, LJ_MIN_GLOBAL)));
|
||||
settabV(L, registry(L), lj_tab_new(L, 0, LJ_MIN_REGISTRY));
|
||||
lj_str_resize(L, LJ_MIN_STRTAB-1);
|
||||
lj_meta_init(L);
|
||||
lj_lex_init(L);
|
||||
fixstring(lj_err_str(L, LJ_ERR_ERRMEM)); /* Preallocate memory error msg. */
|
||||
g->gc.threshold = 4*g->gc.total;
|
||||
lj_trace_initstate(g);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void close_state(lua_State *L)
|
||||
{
|
||||
global_State *g = G(L);
|
||||
lj_func_closeuv(L, tvref(L->stack));
|
||||
lj_gc_freeall(g);
|
||||
lua_assert(gcref(g->gc.root) == obj2gco(L));
|
||||
lua_assert(g->strnum == 0);
|
||||
lj_trace_freestate(g);
|
||||
#if LJ_HASFFI
|
||||
lj_ctype_freestate(g);
|
||||
#endif
|
||||
lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef);
|
||||
lj_str_freebuf(g, &g->tmpbuf);
|
||||
lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue);
|
||||
lua_assert(g->gc.total == sizeof(GG_State));
|
||||
#ifndef LUAJIT_USE_SYSMALLOC
|
||||
if (g->allocf == lj_alloc_f)
|
||||
lj_alloc_destroy(g->allocd);
|
||||
else
|
||||
#endif
|
||||
g->allocf(g->allocd, G2GG(g), sizeof(GG_State), 0);
|
||||
}
|
||||
|
||||
#if LJ_64
|
||||
lua_State *lj_state_newstate(lua_Alloc f, void *ud)
|
||||
#else
|
||||
LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
|
||||
#endif
|
||||
{
|
||||
GG_State *GG = (GG_State *)f(ud, NULL, 0, sizeof(GG_State));
|
||||
lua_State *L = &GG->L;
|
||||
global_State *g = &GG->g;
|
||||
if (GG == NULL || !checkptr32(GG)) return NULL;
|
||||
memset(GG, 0, sizeof(GG_State));
|
||||
L->gct = ~LJ_TTHREAD;
|
||||
L->marked = LJ_GC_WHITE0 | LJ_GC_FIXED | LJ_GC_SFIXED; /* Prevent free. */
|
||||
L->dummy_ffid = FF_C;
|
||||
setmref(L->glref, g);
|
||||
g->gc.currentwhite = LJ_GC_WHITE0 | LJ_GC_FIXED;
|
||||
g->strempty.marked = LJ_GC_WHITE0;
|
||||
g->strempty.gct = ~LJ_TSTR;
|
||||
g->allocf = f;
|
||||
g->allocd = ud;
|
||||
setgcref(g->mainthref, obj2gco(L));
|
||||
setgcref(g->uvhead.prev, obj2gco(&g->uvhead));
|
||||
setgcref(g->uvhead.next, obj2gco(&g->uvhead));
|
||||
g->strmask = ~(MSize)0;
|
||||
setnilV(registry(L));
|
||||
setnilV(&g->nilnode.val);
|
||||
setnilV(&g->nilnode.key);
|
||||
setmref(g->nilnode.freetop, &g->nilnode);
|
||||
lj_str_initbuf(&g->tmpbuf);
|
||||
g->gc.state = GCSpause;
|
||||
setgcref(g->gc.root, obj2gco(L));
|
||||
setmref(g->gc.sweep, &g->gc.root);
|
||||
g->gc.total = sizeof(GG_State);
|
||||
g->gc.pause = LUAI_GCPAUSE;
|
||||
g->gc.stepmul = LUAI_GCMUL;
|
||||
lj_dispatch_init((GG_State *)L);
|
||||
L->status = LUA_ERRERR+1; /* Avoid touching the stack upon memory error. */
|
||||
if (lj_vm_cpcall(L, NULL, NULL, cpluaopen) != 0) {
|
||||
/* Memory allocation error: free partial state. */
|
||||
close_state(L);
|
||||
return NULL;
|
||||
}
|
||||
L->status = 0;
|
||||
return L;
|
||||
}
|
||||
|
||||
static TValue *cpfinalize(lua_State *L, lua_CFunction dummy, void *ud)
|
||||
{
|
||||
UNUSED(dummy);
|
||||
UNUSED(ud);
|
||||
lj_gc_finalize_cdata(L);
|
||||
lj_gc_finalize_udata(L);
|
||||
/* Frame pop omitted. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LUA_API void lua_close(lua_State *L)
|
||||
{
|
||||
global_State *g = G(L);
|
||||
int i;
|
||||
L = mainthread(g); /* Only the main thread can be closed. */
|
||||
lj_func_closeuv(L, tvref(L->stack));
|
||||
lj_gc_separateudata(g, 1); /* Separate udata which have GC metamethods. */
|
||||
#if LJ_HASJIT
|
||||
G2J(g)->flags &= ~JIT_F_ON;
|
||||
G2J(g)->state = LJ_TRACE_IDLE;
|
||||
lj_dispatch_update(g);
|
||||
#endif
|
||||
for (i = 0;;) {
|
||||
hook_enter(g);
|
||||
L->status = 0;
|
||||
L->cframe = NULL;
|
||||
L->base = L->top = tvref(L->stack) + 1;
|
||||
if (lj_vm_cpcall(L, NULL, NULL, cpfinalize) == 0) {
|
||||
if (++i >= 10) break;
|
||||
lj_gc_separateudata(g, 1); /* Separate udata again. */
|
||||
if (gcref(g->gc.mmudata) == NULL) /* Until nothing is left to do. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
close_state(L);
|
||||
}
|
||||
|
||||
lua_State *lj_state_new(lua_State *L)
|
||||
{
|
||||
lua_State *L1 = lj_mem_newobj(L, lua_State);
|
||||
L1->gct = ~LJ_TTHREAD;
|
||||
L1->dummy_ffid = FF_C;
|
||||
L1->status = 0;
|
||||
L1->stacksize = 0;
|
||||
setmref(L1->stack, NULL);
|
||||
L1->cframe = NULL;
|
||||
/* NOBARRIER: The lua_State is new (marked white). */
|
||||
setgcrefnull(L1->openupval);
|
||||
setmrefr(L1->glref, L->glref);
|
||||
setgcrefr(L1->env, L->env);
|
||||
stack_init(L1, L); /* init stack */
|
||||
lua_assert(iswhite(obj2gco(L1)));
|
||||
return L1;
|
||||
}
|
||||
|
||||
void LJ_FASTCALL lj_state_free(global_State *g, lua_State *L)
|
||||
{
|
||||
lua_assert(L != mainthread(g));
|
||||
lj_func_closeuv(L, tvref(L->stack));
|
||||
lua_assert(gcref(L->openupval) == NULL);
|
||||
lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue);
|
||||
lj_mem_freet(g, L);
|
||||
}
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
/*
|
||||
** String handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define lj_str_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_char.h"
|
||||
|
||||
/* -- String interning ---------------------------------------------------- */
|
||||
|
||||
/* Ordered compare of strings. Assumes string data is 4-byte aligned. */
|
||||
int32_t LJ_FASTCALL lj_str_cmp(GCstr *a, GCstr *b)
|
||||
{
|
||||
MSize i, n = a->len > b->len ? b->len : a->len;
|
||||
for (i = 0; i < n; i += 4) {
|
||||
/* Note: innocuous access up to end of string + 3. */
|
||||
uint32_t va = *(const uint32_t *)(strdata(a)+i);
|
||||
uint32_t vb = *(const uint32_t *)(strdata(b)+i);
|
||||
if (va != vb) {
|
||||
#if LJ_LE
|
||||
va = lj_bswap(va); vb = lj_bswap(vb);
|
||||
#endif
|
||||
i -= n;
|
||||
if ((int32_t)i >= -3) {
|
||||
va >>= 32+(i<<3); vb >>= 32+(i<<3);
|
||||
if (va == vb) break;
|
||||
}
|
||||
return va < vb ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return (int32_t)(a->len - b->len);
|
||||
}
|
||||
|
||||
/* Fast string data comparison. Caveat: unaligned access to 1st string! */
|
||||
static LJ_AINLINE int str_fastcmp(const char *a, const char *b, MSize len)
|
||||
{
|
||||
MSize i = 0;
|
||||
lua_assert(len > 0);
|
||||
lua_assert((((uintptr_t)a + len) & (LJ_PAGESIZE-1)) <= LJ_PAGESIZE-4);
|
||||
do { /* Note: innocuous access up to end of string + 3. */
|
||||
uint32_t v = lj_getu32(a+i) ^ *(const uint32_t *)(b+i);
|
||||
if (v) {
|
||||
i -= len;
|
||||
#if LJ_LE
|
||||
return (int32_t)i >= -3 ? (v << (32+(i<<3))) : 1;
|
||||
#else
|
||||
return (int32_t)i >= -3 ? (v >> (32+(i<<3))) : 1;
|
||||
#endif
|
||||
}
|
||||
i += 4;
|
||||
} while (i < len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Resize the string hash table (grow and shrink). */
|
||||
void lj_str_resize(lua_State *L, MSize newmask)
|
||||
{
|
||||
global_State *g = G(L);
|
||||
GCRef *newhash;
|
||||
MSize i;
|
||||
if (g->gc.state == GCSsweepstring || newmask >= LJ_MAX_STRTAB-1)
|
||||
return; /* No resizing during GC traversal or if already too big. */
|
||||
newhash = lj_mem_newvec(L, newmask+1, GCRef);
|
||||
memset(newhash, 0, (newmask+1)*sizeof(GCRef));
|
||||
for (i = g->strmask; i != ~(MSize)0; i--) { /* Rehash old table. */
|
||||
GCobj *p = gcref(g->strhash[i]);
|
||||
while (p) { /* Follow each hash chain and reinsert all strings. */
|
||||
MSize h = gco2str(p)->hash & newmask;
|
||||
GCobj *next = gcnext(p);
|
||||
/* NOBARRIER: The string table is a GC root. */
|
||||
setgcrefr(p->gch.nextgc, newhash[h]);
|
||||
setgcref(newhash[h], p);
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef);
|
||||
g->strmask = newmask;
|
||||
g->strhash = newhash;
|
||||
}
|
||||
|
||||
/* Intern a string and return string object. */
|
||||
GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
|
||||
{
|
||||
global_State *g;
|
||||
GCstr *s;
|
||||
GCobj *o;
|
||||
MSize len = (MSize)lenx;
|
||||
MSize a, b, h = len;
|
||||
if (lenx >= LJ_MAX_STR)
|
||||
lj_err_msg(L, LJ_ERR_STROV);
|
||||
g = G(L);
|
||||
/* Compute string hash. Constants taken from lookup3 hash by Bob Jenkins. */
|
||||
if (len >= 4) { /* Caveat: unaligned access! */
|
||||
a = lj_getu32(str);
|
||||
h ^= lj_getu32(str+len-4);
|
||||
b = lj_getu32(str+(len>>1)-2);
|
||||
h ^= b; h -= lj_rol(b, 14);
|
||||
b += lj_getu32(str+(len>>2)-1);
|
||||
} else if (len > 0) {
|
||||
a = *(const uint8_t *)str;
|
||||
h ^= *(const uint8_t *)(str+len-1);
|
||||
b = *(const uint8_t *)(str+(len>>1));
|
||||
h ^= b; h -= lj_rol(b, 14);
|
||||
} else {
|
||||
return &g->strempty;
|
||||
}
|
||||
a ^= h; a -= lj_rol(h, 11);
|
||||
b ^= a; b -= lj_rol(a, 25);
|
||||
h ^= b; h -= lj_rol(b, 16);
|
||||
/* Check if the string has already been interned. */
|
||||
o = gcref(g->strhash[h & g->strmask]);
|
||||
if (LJ_LIKELY((((uintptr_t)str + len) & (LJ_PAGESIZE-1)) <= LJ_PAGESIZE-4)) {
|
||||
while (o != NULL) {
|
||||
GCstr *sx = gco2str(o);
|
||||
if (sx->len == len && str_fastcmp(str, strdata(sx), len) == 0) {
|
||||
/* Resurrect if dead. Can only happen with fixstring() (keywords). */
|
||||
if (isdead(g, o)) flipwhite(o);
|
||||
return sx; /* Return existing string. */
|
||||
}
|
||||
o = gcnext(o);
|
||||
}
|
||||
} else { /* Slow path: end of string is too close to a page boundary. */
|
||||
while (o != NULL) {
|
||||
GCstr *sx = gco2str(o);
|
||||
if (sx->len == len && memcmp(str, strdata(sx), len) == 0) {
|
||||
/* Resurrect if dead. Can only happen with fixstring() (keywords). */
|
||||
if (isdead(g, o)) flipwhite(o);
|
||||
return sx; /* Return existing string. */
|
||||
}
|
||||
o = gcnext(o);
|
||||
}
|
||||
}
|
||||
/* Nope, create a new string. */
|
||||
s = lj_mem_newt(L, sizeof(GCstr)+len+1, GCstr);
|
||||
newwhite(g, s);
|
||||
s->gct = ~LJ_TSTR;
|
||||
s->len = len;
|
||||
s->hash = h;
|
||||
s->reserved = 0;
|
||||
memcpy(strdatawr(s), str, len);
|
||||
strdatawr(s)[len] = '\0'; /* Zero-terminate string. */
|
||||
/* Add it to string hash table. */
|
||||
h &= g->strmask;
|
||||
s->nextgc = g->strhash[h];
|
||||
/* NOBARRIER: The string table is a GC root. */
|
||||
setgcref(g->strhash[h], obj2gco(s));
|
||||
if (g->strnum++ > g->strmask) /* Allow a 100% load factor. */
|
||||
lj_str_resize(L, (g->strmask<<1)+1); /* Grow string table. */
|
||||
return s; /* Return newly interned string. */
|
||||
}
|
||||
|
||||
void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s)
|
||||
{
|
||||
g->strnum--;
|
||||
lj_mem_free(g, s, sizestring(s));
|
||||
}
|
||||
|
||||
/* -- Type conversions ---------------------------------------------------- */
|
||||
|
||||
/* Print number to buffer. Canonicalizes non-finite values. */
|
||||
size_t LJ_FASTCALL lj_str_bufnum(char *s, cTValue *o)
|
||||
{
|
||||
if (LJ_LIKELY((o->u32.hi << 1) < 0xffe00000)) { /* Finite? */
|
||||
lua_Number n = o->n;
|
||||
#if __BIONIC__
|
||||
if (tvismzero(o)) { s[0] = '-'; s[1] = '0'; return 2; }
|
||||
#endif
|
||||
return (size_t)lua_number2str(s, n);
|
||||
} else if (((o->u32.hi & 0x000fffff) | o->u32.lo) != 0) {
|
||||
s[0] = 'n'; s[1] = 'a'; s[2] = 'n'; return 3;
|
||||
} else if ((o->u32.hi & 0x80000000) == 0) {
|
||||
s[0] = 'i'; s[1] = 'n'; s[2] = 'f'; return 3;
|
||||
} else {
|
||||
s[0] = '-'; s[1] = 'i'; s[2] = 'n'; s[3] = 'f'; return 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print integer to buffer. Returns pointer to start. */
|
||||
char * LJ_FASTCALL lj_str_bufint(char *p, int32_t k)
|
||||
{
|
||||
uint32_t u = (uint32_t)(k < 0 ? -k : k);
|
||||
p += 1+10;
|
||||
do { *--p = (char)('0' + u % 10); } while (u /= 10);
|
||||
if (k < 0) *--p = '-';
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Convert number to string. */
|
||||
GCstr * LJ_FASTCALL lj_str_fromnum(lua_State *L, const lua_Number *np)
|
||||
{
|
||||
char buf[LJ_STR_NUMBUF];
|
||||
size_t len = lj_str_bufnum(buf, (TValue *)np);
|
||||
return lj_str_new(L, buf, len);
|
||||
}
|
||||
|
||||
/* Convert integer to string. */
|
||||
GCstr * LJ_FASTCALL lj_str_fromint(lua_State *L, int32_t k)
|
||||
{
|
||||
char s[1+10];
|
||||
char *p = lj_str_bufint(s, k);
|
||||
return lj_str_new(L, p, (size_t)(s+sizeof(s)-p));
|
||||
}
|
||||
|
||||
GCstr * LJ_FASTCALL lj_str_fromnumber(lua_State *L, cTValue *o)
|
||||
{
|
||||
return tvisint(o) ? lj_str_fromint(L, intV(o)) : lj_str_fromnum(L, &o->n);
|
||||
}
|
||||
|
||||
/* -- String formatting --------------------------------------------------- */
|
||||
|
||||
static void addstr(lua_State *L, SBuf *sb, const char *str, MSize len)
|
||||
{
|
||||
char *p;
|
||||
MSize i;
|
||||
if (sb->n + len > sb->sz) {
|
||||
MSize sz = sb->sz * 2;
|
||||
while (sb->n + len > sz) sz = sz * 2;
|
||||
lj_str_resizebuf(L, sb, sz);
|
||||
}
|
||||
p = sb->buf + sb->n;
|
||||
sb->n += len;
|
||||
for (i = 0; i < len; i++) p[i] = str[i];
|
||||
}
|
||||
|
||||
static void addchar(lua_State *L, SBuf *sb, int c)
|
||||
{
|
||||
if (sb->n + 1 > sb->sz) {
|
||||
MSize sz = sb->sz * 2;
|
||||
lj_str_resizebuf(L, sb, sz);
|
||||
}
|
||||
sb->buf[sb->n++] = (char)c;
|
||||
}
|
||||
|
||||
/* Push formatted message as a string object to Lua stack. va_list variant. */
|
||||
const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp)
|
||||
{
|
||||
SBuf *sb = &G(L)->tmpbuf;
|
||||
lj_str_needbuf(L, sb, (MSize)strlen(fmt));
|
||||
lj_str_resetbuf(sb);
|
||||
for (;;) {
|
||||
const char *e = strchr(fmt, '%');
|
||||
if (e == NULL) break;
|
||||
addstr(L, sb, fmt, (MSize)(e-fmt));
|
||||
/* This function only handles %s, %c, %d, %f and %p formats. */
|
||||
switch (e[1]) {
|
||||
case 's': {
|
||||
const char *s = va_arg(argp, char *);
|
||||
if (s == NULL) s = "(null)";
|
||||
addstr(L, sb, s, (MSize)strlen(s));
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
addchar(L, sb, va_arg(argp, int));
|
||||
break;
|
||||
case 'd': {
|
||||
char buf[LJ_STR_INTBUF];
|
||||
char *p = lj_str_bufint(buf, va_arg(argp, int32_t));
|
||||
addstr(L, sb, p, (MSize)(buf+LJ_STR_INTBUF-p));
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
char buf[LJ_STR_NUMBUF];
|
||||
TValue tv;
|
||||
MSize len;
|
||||
tv.n = (lua_Number)(va_arg(argp, LUAI_UACNUMBER));
|
||||
len = (MSize)lj_str_bufnum(buf, &tv);
|
||||
addstr(L, sb, buf, len);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
#define FMTP_CHARS (2*sizeof(ptrdiff_t))
|
||||
char buf[2+FMTP_CHARS];
|
||||
ptrdiff_t p = (ptrdiff_t)(va_arg(argp, void *));
|
||||
ptrdiff_t i, lasti = 2+FMTP_CHARS;
|
||||
if (p == 0) {
|
||||
addstr(L, sb, "NULL", 4);
|
||||
break;
|
||||
}
|
||||
#if LJ_64
|
||||
/* Shorten output for 64 bit pointers. */
|
||||
lasti = 2+2*4+((p >> 32) ? 2+2*(lj_fls((uint32_t)(p >> 32))>>3) : 0);
|
||||
#endif
|
||||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
for (i = lasti-1; i >= 2; i--, p >>= 4)
|
||||
buf[i] = "0123456789abcdef"[(p & 15)];
|
||||
addstr(L, sb, buf, (MSize)lasti);
|
||||
break;
|
||||
}
|
||||
case '%':
|
||||
addchar(L, sb, '%');
|
||||
break;
|
||||
default:
|
||||
addchar(L, sb, '%');
|
||||
addchar(L, sb, e[1]);
|
||||
break;
|
||||
}
|
||||
fmt = e+2;
|
||||
}
|
||||
addstr(L, sb, fmt, (MSize)strlen(fmt));
|
||||
setstrV(L, L->top, lj_str_new(L, sb->buf, sb->n));
|
||||
incr_top(L);
|
||||
return strVdata(L->top - 1);
|
||||
}
|
||||
|
||||
/* Push formatted message as a string object to Lua stack. Vararg variant. */
|
||||
const char *lj_str_pushf(lua_State *L, const char *fmt, ...)
|
||||
{
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
msg = lj_str_pushvf(L, fmt, argp);
|
||||
va_end(argp);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* -- Buffer handling ----------------------------------------------------- */
|
||||
|
||||
char *lj_str_needbuf(lua_State *L, SBuf *sb, MSize sz)
|
||||
{
|
||||
if (sz > sb->sz) {
|
||||
if (sz < LJ_MIN_SBUF) sz = LJ_MIN_SBUF;
|
||||
lj_str_resizebuf(L, sb, sz);
|
||||
}
|
||||
return sb->buf;
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
** String handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_STR_H
|
||||
#define _LJ_STR_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
/* String interning. */
|
||||
LJ_FUNC int32_t LJ_FASTCALL lj_str_cmp(GCstr *a, GCstr *b);
|
||||
LJ_FUNC void lj_str_resize(lua_State *L, MSize newmask);
|
||||
LJ_FUNCA GCstr *lj_str_new(lua_State *L, const char *str, size_t len);
|
||||
LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s);
|
||||
|
||||
#define lj_str_newz(L, s) (lj_str_new(L, s, strlen(s)))
|
||||
#define lj_str_newlit(L, s) (lj_str_new(L, "" s, sizeof(s)-1))
|
||||
|
||||
/* Type conversions. */
|
||||
LJ_FUNC size_t LJ_FASTCALL lj_str_bufnum(char *s, cTValue *o);
|
||||
LJ_FUNC char * LJ_FASTCALL lj_str_bufint(char *p, int32_t k);
|
||||
LJ_FUNCA GCstr * LJ_FASTCALL lj_str_fromnum(lua_State *L, const lua_Number *np);
|
||||
LJ_FUNC GCstr * LJ_FASTCALL lj_str_fromint(lua_State *L, int32_t k);
|
||||
LJ_FUNCA GCstr * LJ_FASTCALL lj_str_fromnumber(lua_State *L, cTValue *o);
|
||||
|
||||
#define LJ_STR_INTBUF (1+10)
|
||||
#define LJ_STR_NUMBUF LUAI_MAXNUMBER2STR
|
||||
|
||||
/* String formatting. */
|
||||
LJ_FUNC const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp);
|
||||
LJ_FUNC const char *lj_str_pushf(lua_State *L, const char *fmt, ...)
|
||||
#if defined(__GNUC__)
|
||||
__attribute__ ((format (printf, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
/* Resizable string buffers. Struct definition in lj_obj.h. */
|
||||
LJ_FUNC char *lj_str_needbuf(lua_State *L, SBuf *sb, MSize sz);
|
||||
|
||||
#define lj_str_initbuf(sb) ((sb)->buf = NULL, (sb)->sz = 0)
|
||||
#define lj_str_resetbuf(sb) ((sb)->n = 0)
|
||||
#define lj_str_resizebuf(L, sb, size) \
|
||||
((sb)->buf = (char *)lj_mem_realloc(L, (sb)->buf, (sb)->sz, (size)), \
|
||||
(sb)->sz = (size))
|
||||
#define lj_str_freebuf(g, sb) lj_mem_free(g, (void *)(sb)->buf, (sb)->sz)
|
||||
|
||||
#endif
|
||||
@@ -1,624 +0,0 @@
|
||||
/*
|
||||
** Table handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lj_tab_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_tab.h"
|
||||
|
||||
/* -- Object hashing ------------------------------------------------------ */
|
||||
|
||||
/* Hash values are masked with the table hash mask and used as an index. */
|
||||
static LJ_AINLINE Node *hashmask(const GCtab *t, uint32_t hash)
|
||||
{
|
||||
Node *n = noderef(t->node);
|
||||
return &n[hash & t->hmask];
|
||||
}
|
||||
|
||||
/* String hashes are precomputed when they are interned. */
|
||||
#define hashstr(t, s) hashmask(t, (s)->hash)
|
||||
|
||||
#define hashlohi(t, lo, hi) hashmask((t), hashrot((lo), (hi)))
|
||||
#define hashnum(t, o) hashlohi((t), (o)->u32.lo, ((o)->u32.hi << 1))
|
||||
#define hashptr(t, p) hashlohi((t), u32ptr(p), u32ptr(p) + HASH_BIAS)
|
||||
#define hashgcref(t, r) hashlohi((t), gcrefu(r), gcrefu(r) + HASH_BIAS)
|
||||
|
||||
/* Hash an arbitrary key and return its anchor position in the hash table. */
|
||||
static Node *hashkey(const GCtab *t, cTValue *key)
|
||||
{
|
||||
lua_assert(!tvisint(key));
|
||||
if (tvisstr(key))
|
||||
return hashstr(t, strV(key));
|
||||
else if (tvisnum(key))
|
||||
return hashnum(t, key);
|
||||
else if (tvisbool(key))
|
||||
return hashmask(t, boolV(key));
|
||||
else
|
||||
return hashgcref(t, key->gcr);
|
||||
/* Only hash 32 bits of lightuserdata on a 64 bit CPU. Good enough? */
|
||||
}
|
||||
|
||||
/* -- Table creation and destruction -------------------------------------- */
|
||||
|
||||
/* Create new hash part for table. */
|
||||
static LJ_AINLINE void newhpart(lua_State *L, GCtab *t, uint32_t hbits)
|
||||
{
|
||||
uint32_t hsize;
|
||||
Node *node;
|
||||
lua_assert(hbits != 0);
|
||||
if (hbits > LJ_MAX_HBITS)
|
||||
lj_err_msg(L, LJ_ERR_TABOV);
|
||||
hsize = 1u << hbits;
|
||||
node = lj_mem_newvec(L, hsize, Node);
|
||||
setmref(node->freetop, &node[hsize]);
|
||||
setmref(t->node, node);
|
||||
t->hmask = hsize-1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Q: Why all of these copies of t->hmask, t->node etc. to local variables?
|
||||
** A: Because alias analysis for C is _really_ tough.
|
||||
** Even state-of-the-art C compilers won't produce good code without this.
|
||||
*/
|
||||
|
||||
/* Clear hash part of table. */
|
||||
static LJ_AINLINE void clearhpart(GCtab *t)
|
||||
{
|
||||
uint32_t i, hmask = t->hmask;
|
||||
Node *node = noderef(t->node);
|
||||
lua_assert(t->hmask != 0);
|
||||
for (i = 0; i <= hmask; i++) {
|
||||
Node *n = &node[i];
|
||||
setmref(n->next, NULL);
|
||||
setnilV(&n->key);
|
||||
setnilV(&n->val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear array part of table. */
|
||||
static LJ_AINLINE void clearapart(GCtab *t)
|
||||
{
|
||||
uint32_t i, asize = t->asize;
|
||||
TValue *array = tvref(t->array);
|
||||
for (i = 0; i < asize; i++)
|
||||
setnilV(&array[i]);
|
||||
}
|
||||
|
||||
/* Create a new table. Note: the slots are not initialized (yet). */
|
||||
static GCtab *newtab(lua_State *L, uint32_t asize, uint32_t hbits)
|
||||
{
|
||||
GCtab *t;
|
||||
/* First try to colocate the array part. */
|
||||
if (LJ_MAX_COLOSIZE != 0 && asize > 0 && asize <= LJ_MAX_COLOSIZE) {
|
||||
lua_assert((sizeof(GCtab) & 7) == 0);
|
||||
t = (GCtab *)lj_mem_newgco(L, sizetabcolo(asize));
|
||||
t->gct = ~LJ_TTAB;
|
||||
t->nomm = (uint8_t)~0;
|
||||
t->colo = (int8_t)asize;
|
||||
setmref(t->array, (TValue *)((char *)t + sizeof(GCtab)));
|
||||
setgcrefnull(t->metatable);
|
||||
t->asize = asize;
|
||||
t->hmask = 0;
|
||||
setmref(t->node, &G(L)->nilnode);
|
||||
} else { /* Otherwise separately allocate the array part. */
|
||||
t = lj_mem_newobj(L, GCtab);
|
||||
t->gct = ~LJ_TTAB;
|
||||
t->nomm = (uint8_t)~0;
|
||||
t->colo = 0;
|
||||
setmref(t->array, NULL);
|
||||
setgcrefnull(t->metatable);
|
||||
t->asize = 0; /* In case the array allocation fails. */
|
||||
t->hmask = 0;
|
||||
setmref(t->node, &G(L)->nilnode);
|
||||
if (asize > 0) {
|
||||
if (asize > LJ_MAX_ASIZE)
|
||||
lj_err_msg(L, LJ_ERR_TABOV);
|
||||
setmref(t->array, lj_mem_newvec(L, asize, TValue));
|
||||
t->asize = asize;
|
||||
}
|
||||
}
|
||||
if (hbits)
|
||||
newhpart(L, t, hbits);
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Create a new table.
|
||||
**
|
||||
** IMPORTANT NOTE: The API differs from lua_createtable()!
|
||||
**
|
||||
** The array size is non-inclusive. E.g. asize=128 creates array slots
|
||||
** for 0..127, but not for 128. If you need slots 1..128, pass asize=129
|
||||
** (slot 0 is wasted in this case).
|
||||
**
|
||||
** The hash size is given in hash bits. hbits=0 means no hash part.
|
||||
** hbits=1 creates 2 hash slots, hbits=2 creates 4 hash slots and so on.
|
||||
*/
|
||||
GCtab *lj_tab_new(lua_State *L, uint32_t asize, uint32_t hbits)
|
||||
{
|
||||
GCtab *t = newtab(L, asize, hbits);
|
||||
clearapart(t);
|
||||
if (t->hmask > 0) clearhpart(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
#if LJ_HASJIT
|
||||
GCtab * LJ_FASTCALL lj_tab_new1(lua_State *L, uint32_t ahsize)
|
||||
{
|
||||
GCtab *t = newtab(L, ahsize & 0xffffff, ahsize >> 24);
|
||||
clearapart(t);
|
||||
if (t->hmask > 0) clearhpart(t);
|
||||
return t;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Duplicate a table. */
|
||||
GCtab * LJ_FASTCALL lj_tab_dup(lua_State *L, const GCtab *kt)
|
||||
{
|
||||
GCtab *t;
|
||||
uint32_t asize, hmask;
|
||||
t = newtab(L, kt->asize, kt->hmask > 0 ? lj_fls(kt->hmask)+1 : 0);
|
||||
lua_assert(kt->asize == t->asize && kt->hmask == t->hmask);
|
||||
t->nomm = 0; /* Keys with metamethod names may be present. */
|
||||
asize = kt->asize;
|
||||
if (asize > 0) {
|
||||
TValue *array = tvref(t->array);
|
||||
TValue *karray = tvref(kt->array);
|
||||
if (asize < 64) { /* An inlined loop beats memcpy for < 512 bytes. */
|
||||
uint32_t i;
|
||||
for (i = 0; i < asize; i++)
|
||||
copyTV(L, &array[i], &karray[i]);
|
||||
} else {
|
||||
memcpy(array, karray, asize*sizeof(TValue));
|
||||
}
|
||||
}
|
||||
hmask = kt->hmask;
|
||||
if (hmask > 0) {
|
||||
uint32_t i;
|
||||
Node *node = noderef(t->node);
|
||||
Node *knode = noderef(kt->node);
|
||||
ptrdiff_t d = (char *)node - (char *)knode;
|
||||
setmref(node->freetop, (Node *)((char *)noderef(knode->freetop) + d));
|
||||
for (i = 0; i <= hmask; i++) {
|
||||
Node *kn = &knode[i];
|
||||
Node *n = &node[i];
|
||||
Node *next = nextnode(kn);
|
||||
/* Don't use copyTV here, since it asserts on a copy of a dead key. */
|
||||
n->val = kn->val; n->key = kn->key;
|
||||
setmref(n->next, next == NULL? next : (Node *)((char *)next + d));
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Free a table. */
|
||||
void LJ_FASTCALL lj_tab_free(global_State *g, GCtab *t)
|
||||
{
|
||||
if (t->hmask > 0)
|
||||
lj_mem_freevec(g, noderef(t->node), t->hmask+1, Node);
|
||||
if (t->asize > 0 && LJ_MAX_COLOSIZE != 0 && t->colo <= 0)
|
||||
lj_mem_freevec(g, tvref(t->array), t->asize, TValue);
|
||||
if (LJ_MAX_COLOSIZE != 0 && t->colo)
|
||||
lj_mem_free(g, t, sizetabcolo((uint32_t)t->colo & 0x7f));
|
||||
else
|
||||
lj_mem_freet(g, t);
|
||||
}
|
||||
|
||||
/* -- Table resizing ------------------------------------------------------ */
|
||||
|
||||
/* Resize a table to fit the new array/hash part sizes. */
|
||||
static void resizetab(lua_State *L, GCtab *t, uint32_t asize, uint32_t hbits)
|
||||
{
|
||||
Node *oldnode = noderef(t->node);
|
||||
uint32_t oldasize = t->asize;
|
||||
uint32_t oldhmask = t->hmask;
|
||||
if (asize > oldasize) { /* Array part grows? */
|
||||
TValue *array;
|
||||
uint32_t i;
|
||||
if (asize > LJ_MAX_ASIZE)
|
||||
lj_err_msg(L, LJ_ERR_TABOV);
|
||||
if (LJ_MAX_COLOSIZE != 0 && t->colo > 0) {
|
||||
/* A colocated array must be separated and copied. */
|
||||
TValue *oarray = tvref(t->array);
|
||||
array = lj_mem_newvec(L, asize, TValue);
|
||||
t->colo = (int8_t)(t->colo | 0x80); /* Mark as separated (colo < 0). */
|
||||
for (i = 0; i < oldasize; i++)
|
||||
copyTV(L, &array[i], &oarray[i]);
|
||||
} else {
|
||||
array = (TValue *)lj_mem_realloc(L, tvref(t->array),
|
||||
oldasize*sizeof(TValue), asize*sizeof(TValue));
|
||||
}
|
||||
setmref(t->array, array);
|
||||
t->asize = asize;
|
||||
for (i = oldasize; i < asize; i++) /* Clear newly allocated slots. */
|
||||
setnilV(&array[i]);
|
||||
}
|
||||
/* Create new (empty) hash part. */
|
||||
if (hbits) {
|
||||
newhpart(L, t, hbits);
|
||||
clearhpart(t);
|
||||
} else {
|
||||
global_State *g = G(L);
|
||||
setmref(t->node, &g->nilnode);
|
||||
t->hmask = 0;
|
||||
}
|
||||
if (asize < oldasize) { /* Array part shrinks? */
|
||||
TValue *array = tvref(t->array);
|
||||
uint32_t i;
|
||||
t->asize = asize; /* Note: This 'shrinks' even colocated arrays. */
|
||||
for (i = asize; i < oldasize; i++) /* Reinsert old array values. */
|
||||
if (!tvisnil(&array[i]))
|
||||
copyTV(L, lj_tab_setinth(L, t, (int32_t)i), &array[i]);
|
||||
/* Physically shrink only separated arrays. */
|
||||
if (LJ_MAX_COLOSIZE != 0 && t->colo <= 0)
|
||||
setmref(t->array, lj_mem_realloc(L, array,
|
||||
oldasize*sizeof(TValue), asize*sizeof(TValue)));
|
||||
}
|
||||
if (oldhmask > 0) { /* Reinsert pairs from old hash part. */
|
||||
global_State *g;
|
||||
uint32_t i;
|
||||
for (i = 0; i <= oldhmask; i++) {
|
||||
Node *n = &oldnode[i];
|
||||
if (!tvisnil(&n->val))
|
||||
copyTV(L, lj_tab_set(L, t, &n->key), &n->val);
|
||||
}
|
||||
g = G(L);
|
||||
lj_mem_freevec(g, oldnode, oldhmask+1, Node);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t countint(cTValue *key, uint32_t *bins)
|
||||
{
|
||||
lua_assert(!tvisint(key));
|
||||
if (tvisnum(key)) {
|
||||
lua_Number nk = numV(key);
|
||||
int32_t k = lj_num2int(nk);
|
||||
if ((uint32_t)k < LJ_MAX_ASIZE && nk == (lua_Number)k) {
|
||||
bins[(k > 2 ? lj_fls((uint32_t)(k-1)) : 0)]++;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t countarray(const GCtab *t, uint32_t *bins)
|
||||
{
|
||||
uint32_t na, b, i;
|
||||
if (t->asize == 0) return 0;
|
||||
for (na = i = b = 0; b < LJ_MAX_ABITS; b++) {
|
||||
uint32_t n, top = 2u << b;
|
||||
TValue *array;
|
||||
if (top >= t->asize) {
|
||||
top = t->asize-1;
|
||||
if (i > top)
|
||||
break;
|
||||
}
|
||||
array = tvref(t->array);
|
||||
for (n = 0; i <= top; i++)
|
||||
if (!tvisnil(&array[i]))
|
||||
n++;
|
||||
bins[b] += n;
|
||||
na += n;
|
||||
}
|
||||
return na;
|
||||
}
|
||||
|
||||
static uint32_t counthash(const GCtab *t, uint32_t *bins, uint32_t *narray)
|
||||
{
|
||||
uint32_t total, na, i, hmask = t->hmask;
|
||||
Node *node = noderef(t->node);
|
||||
for (total = na = 0, i = 0; i <= hmask; i++) {
|
||||
Node *n = &node[i];
|
||||
if (!tvisnil(&n->val)) {
|
||||
na += countint(&n->key, bins);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
*narray += na;
|
||||
return total;
|
||||
}
|
||||
|
||||
static uint32_t bestasize(uint32_t bins[], uint32_t *narray)
|
||||
{
|
||||
uint32_t b, sum, na = 0, sz = 0, nn = *narray;
|
||||
for (b = 0, sum = 0; 2*nn > (1u<<b) && sum != nn; b++)
|
||||
if (bins[b] > 0 && 2*(sum += bins[b]) > (1u<<b)) {
|
||||
sz = (2u<<b)+1;
|
||||
na = sum;
|
||||
}
|
||||
*narray = sz;
|
||||
return na;
|
||||
}
|
||||
|
||||
static void rehashtab(lua_State *L, GCtab *t, cTValue *ek)
|
||||
{
|
||||
uint32_t bins[LJ_MAX_ABITS];
|
||||
uint32_t total, asize, na, i;
|
||||
for (i = 0; i < LJ_MAX_ABITS; i++) bins[i] = 0;
|
||||
asize = countarray(t, bins);
|
||||
total = 1 + asize;
|
||||
total += counthash(t, bins, &asize);
|
||||
asize += countint(ek, bins);
|
||||
na = bestasize(bins, &asize);
|
||||
total -= na;
|
||||
resizetab(L, t, asize, hsize2hbits(total));
|
||||
}
|
||||
|
||||
void lj_tab_reasize(lua_State *L, GCtab *t, uint32_t nasize)
|
||||
{
|
||||
resizetab(L, t, nasize+1, t->hmask > 0 ? lj_fls(t->hmask)+1 : 0);
|
||||
}
|
||||
|
||||
/* -- Table getters ------------------------------------------------------- */
|
||||
|
||||
cTValue * LJ_FASTCALL lj_tab_getinth(GCtab *t, int32_t key)
|
||||
{
|
||||
TValue k;
|
||||
Node *n;
|
||||
k.n = (lua_Number)key;
|
||||
n = hashnum(t, &k);
|
||||
do {
|
||||
if (tvisnum(&n->key) && n->key.n == k.n)
|
||||
return &n->val;
|
||||
} while ((n = nextnode(n)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cTValue *lj_tab_getstr(GCtab *t, GCstr *key)
|
||||
{
|
||||
Node *n = hashstr(t, key);
|
||||
do {
|
||||
if (tvisstr(&n->key) && strV(&n->key) == key)
|
||||
return &n->val;
|
||||
} while ((n = nextnode(n)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cTValue *lj_tab_get(lua_State *L, GCtab *t, cTValue *key)
|
||||
{
|
||||
if (tvisstr(key)) {
|
||||
cTValue *tv = lj_tab_getstr(t, strV(key));
|
||||
if (tv)
|
||||
return tv;
|
||||
} else if (tvisint(key)) {
|
||||
cTValue *tv = lj_tab_getint(t, intV(key));
|
||||
if (tv)
|
||||
return tv;
|
||||
} else if (tvisnum(key)) {
|
||||
lua_Number nk = numV(key);
|
||||
int32_t k = lj_num2int(nk);
|
||||
if (nk == (lua_Number)k) {
|
||||
cTValue *tv = lj_tab_getint(t, k);
|
||||
if (tv)
|
||||
return tv;
|
||||
} else {
|
||||
goto genlookup; /* Else use the generic lookup. */
|
||||
}
|
||||
} else if (!tvisnil(key)) {
|
||||
Node *n;
|
||||
genlookup:
|
||||
n = hashkey(t, key);
|
||||
do {
|
||||
if (lj_obj_equal(&n->key, key))
|
||||
return &n->val;
|
||||
} while ((n = nextnode(n)));
|
||||
}
|
||||
return niltv(L);
|
||||
}
|
||||
|
||||
/* -- Table setters ------------------------------------------------------- */
|
||||
|
||||
/* Insert new key. Use Brent's variation to optimize the chain length. */
|
||||
TValue *lj_tab_newkey(lua_State *L, GCtab *t, cTValue *key)
|
||||
{
|
||||
Node *n = hashkey(t, key);
|
||||
if (!tvisnil(&n->val) || t->hmask == 0) {
|
||||
Node *nodebase = noderef(t->node);
|
||||
Node *collide, *freenode = noderef(nodebase->freetop);
|
||||
lua_assert(freenode >= nodebase && freenode <= nodebase+t->hmask+1);
|
||||
do {
|
||||
if (freenode == nodebase) { /* No free node found? */
|
||||
rehashtab(L, t, key); /* Rehash table. */
|
||||
return lj_tab_set(L, t, key); /* Retry key insertion. */
|
||||
}
|
||||
} while (!tvisnil(&(--freenode)->key));
|
||||
setmref(nodebase->freetop, freenode);
|
||||
lua_assert(freenode != &G(L)->nilnode);
|
||||
collide = hashkey(t, &n->key);
|
||||
if (collide != n) { /* Colliding node not the main node? */
|
||||
while (noderef(collide->next) != n) /* Find predecessor. */
|
||||
collide = nextnode(collide);
|
||||
setmref(collide->next, freenode); /* Relink chain. */
|
||||
/* Copy colliding node into free node and free main node. */
|
||||
freenode->val = n->val;
|
||||
freenode->key = n->key;
|
||||
freenode->next = n->next;
|
||||
setmref(n->next, NULL);
|
||||
setnilV(&n->val);
|
||||
/* Rechain pseudo-resurrected string keys with colliding hashes. */
|
||||
while (nextnode(freenode)) {
|
||||
Node *nn = nextnode(freenode);
|
||||
if (tvisstr(&nn->key) && !tvisnil(&nn->val) &&
|
||||
hashstr(t, strV(&nn->key)) == n) {
|
||||
freenode->next = nn->next;
|
||||
nn->next = n->next;
|
||||
setmref(n->next, nn);
|
||||
} else {
|
||||
freenode = nn;
|
||||
}
|
||||
}
|
||||
} else { /* Otherwise use free node. */
|
||||
setmrefr(freenode->next, n->next); /* Insert into chain. */
|
||||
setmref(n->next, freenode);
|
||||
n = freenode;
|
||||
}
|
||||
}
|
||||
n->key.u64 = key->u64;
|
||||
if (LJ_UNLIKELY(tvismzero(&n->key)))
|
||||
n->key.u64 = 0;
|
||||
lj_gc_anybarriert(L, t);
|
||||
lua_assert(tvisnil(&n->val));
|
||||
return &n->val;
|
||||
}
|
||||
|
||||
TValue *lj_tab_setinth(lua_State *L, GCtab *t, int32_t key)
|
||||
{
|
||||
TValue k;
|
||||
Node *n;
|
||||
k.n = (lua_Number)key;
|
||||
n = hashnum(t, &k);
|
||||
do {
|
||||
if (tvisnum(&n->key) && n->key.n == k.n)
|
||||
return &n->val;
|
||||
} while ((n = nextnode(n)));
|
||||
return lj_tab_newkey(L, t, &k);
|
||||
}
|
||||
|
||||
TValue *lj_tab_setstr(lua_State *L, GCtab *t, GCstr *key)
|
||||
{
|
||||
TValue k;
|
||||
Node *n = hashstr(t, key);
|
||||
do {
|
||||
if (tvisstr(&n->key) && strV(&n->key) == key)
|
||||
return &n->val;
|
||||
} while ((n = nextnode(n)));
|
||||
setstrV(L, &k, key);
|
||||
return lj_tab_newkey(L, t, &k);
|
||||
}
|
||||
|
||||
TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key)
|
||||
{
|
||||
Node *n;
|
||||
t->nomm = 0; /* Invalidate negative metamethod cache. */
|
||||
if (tvisstr(key)) {
|
||||
return lj_tab_setstr(L, t, strV(key));
|
||||
} else if (tvisint(key)) {
|
||||
return lj_tab_setint(L, t, intV(key));
|
||||
} else if (tvisnum(key)) {
|
||||
lua_Number nk = numV(key);
|
||||
int32_t k = lj_num2int(nk);
|
||||
if (nk == (lua_Number)k)
|
||||
return lj_tab_setint(L, t, k);
|
||||
if (tvisnan(key))
|
||||
lj_err_msg(L, LJ_ERR_NANIDX);
|
||||
/* Else use the generic lookup. */
|
||||
} else if (tvisnil(key)) {
|
||||
lj_err_msg(L, LJ_ERR_NILIDX);
|
||||
}
|
||||
n = hashkey(t, key);
|
||||
do {
|
||||
if (lj_obj_equal(&n->key, key))
|
||||
return &n->val;
|
||||
} while ((n = nextnode(n)));
|
||||
return lj_tab_newkey(L, t, key);
|
||||
}
|
||||
|
||||
/* -- Table traversal ----------------------------------------------------- */
|
||||
|
||||
/* Get the traversal index of a key. */
|
||||
static uint32_t keyindex(lua_State *L, GCtab *t, cTValue *key)
|
||||
{
|
||||
TValue tmp;
|
||||
if (tvisint(key)) {
|
||||
int32_t k = intV(key);
|
||||
if ((uint32_t)k < t->asize)
|
||||
return (uint32_t)k; /* Array key indexes: [0..t->asize-1] */
|
||||
setnumV(&tmp, (lua_Number)k);
|
||||
key = &tmp;
|
||||
} else if (tvisnum(key)) {
|
||||
lua_Number nk = numV(key);
|
||||
int32_t k = lj_num2int(nk);
|
||||
if ((uint32_t)k < t->asize && nk == (lua_Number)k)
|
||||
return (uint32_t)k; /* Array key indexes: [0..t->asize-1] */
|
||||
}
|
||||
if (!tvisnil(key)) {
|
||||
Node *n = hashkey(t, key);
|
||||
do {
|
||||
if (lj_obj_equal(&n->key, key))
|
||||
return t->asize + (uint32_t)(n - noderef(t->node));
|
||||
/* Hash key indexes: [t->asize..t->asize+t->nmask] */
|
||||
} while ((n = nextnode(n)));
|
||||
if (key->u32.hi == 0xfffe7fff) /* ITERN was despecialized while running. */
|
||||
return key->u32.lo - 1;
|
||||
lj_err_msg(L, LJ_ERR_NEXTIDX);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
return ~0u; /* A nil key starts the traversal. */
|
||||
}
|
||||
|
||||
/* Advance to the next step in a table traversal. */
|
||||
int lj_tab_next(lua_State *L, GCtab *t, TValue *key)
|
||||
{
|
||||
uint32_t i = keyindex(L, t, key); /* Find predecessor key index. */
|
||||
for (i++; i < t->asize; i++) /* First traverse the array keys. */
|
||||
if (!tvisnil(arrayslot(t, i))) {
|
||||
setintV(key, i);
|
||||
copyTV(L, key+1, arrayslot(t, i));
|
||||
return 1;
|
||||
}
|
||||
for (i -= t->asize; i <= t->hmask; i++) { /* Then traverse the hash keys. */
|
||||
Node *n = &noderef(t->node)[i];
|
||||
if (!tvisnil(&n->val)) {
|
||||
copyTV(L, key, &n->key);
|
||||
copyTV(L, key+1, &n->val);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; /* End of traversal. */
|
||||
}
|
||||
|
||||
/* -- Table length calculation -------------------------------------------- */
|
||||
|
||||
static MSize unbound_search(GCtab *t, MSize j)
|
||||
{
|
||||
cTValue *tv;
|
||||
MSize i = j; /* i is zero or a present index */
|
||||
j++;
|
||||
/* find `i' and `j' such that i is present and j is not */
|
||||
while ((tv = lj_tab_getint(t, (int32_t)j)) && !tvisnil(tv)) {
|
||||
i = j;
|
||||
j *= 2;
|
||||
if (j > (MSize)(INT_MAX-2)) { /* overflow? */
|
||||
/* table was built with bad purposes: resort to linear search */
|
||||
i = 1;
|
||||
while ((tv = lj_tab_getint(t, (int32_t)i)) && !tvisnil(tv)) i++;
|
||||
return i - 1;
|
||||
}
|
||||
}
|
||||
/* now do a binary search between them */
|
||||
while (j - i > 1) {
|
||||
MSize m = (i+j)/2;
|
||||
cTValue *tvb = lj_tab_getint(t, (int32_t)m);
|
||||
if (tvb && !tvisnil(tvb)) i = m; else j = m;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to find a boundary in table `t'. A `boundary' is an integer index
|
||||
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
|
||||
*/
|
||||
MSize LJ_FASTCALL lj_tab_len(GCtab *t)
|
||||
{
|
||||
MSize j = (MSize)t->asize;
|
||||
if (j > 1 && tvisnil(arrayslot(t, j-1))) {
|
||||
MSize i = 1;
|
||||
while (j - i > 1) {
|
||||
MSize m = (i+j)/2;
|
||||
if (tvisnil(arrayslot(t, m-1))) j = m; else i = m;
|
||||
}
|
||||
return i-1;
|
||||
}
|
||||
if (j) j--;
|
||||
if (t->hmask <= 0)
|
||||
return j;
|
||||
return unbound_search(t, j);
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
** Table handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_TAB_H
|
||||
#define _LJ_TAB_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
/* Hash constants. Tuned using a brute force search. */
|
||||
#define HASH_BIAS (-0x04c11db7)
|
||||
#define HASH_ROT1 14
|
||||
#define HASH_ROT2 5
|
||||
#define HASH_ROT3 13
|
||||
|
||||
/* Scramble the bits of numbers and pointers. */
|
||||
static LJ_AINLINE uint32_t hashrot(uint32_t lo, uint32_t hi)
|
||||
{
|
||||
#if LJ_TARGET_X86ORX64
|
||||
/* Prefer variant that compiles well for a 2-operand CPU. */
|
||||
lo ^= hi; hi = lj_rol(hi, HASH_ROT1);
|
||||
lo -= hi; hi = lj_rol(hi, HASH_ROT2);
|
||||
hi ^= lo; hi -= lj_rol(lo, HASH_ROT3);
|
||||
#else
|
||||
lo ^= hi;
|
||||
lo = lo - lj_rol(hi, HASH_ROT1);
|
||||
hi = lo ^ lj_rol(hi, HASH_ROT1 + HASH_ROT2);
|
||||
hi = hi - lj_rol(lo, HASH_ROT3);
|
||||
#endif
|
||||
return hi;
|
||||
}
|
||||
|
||||
#define hsize2hbits(s) ((s) ? ((s)==1 ? 1 : 1+lj_fls((uint32_t)((s)-1))) : 0)
|
||||
|
||||
LJ_FUNCA GCtab *lj_tab_new(lua_State *L, uint32_t asize, uint32_t hbits);
|
||||
#if LJ_HASJIT
|
||||
LJ_FUNC GCtab * LJ_FASTCALL lj_tab_new1(lua_State *L, uint32_t ahsize);
|
||||
#endif
|
||||
LJ_FUNCA GCtab * LJ_FASTCALL lj_tab_dup(lua_State *L, const GCtab *kt);
|
||||
LJ_FUNC void LJ_FASTCALL lj_tab_free(global_State *g, GCtab *t);
|
||||
LJ_FUNCA void lj_tab_reasize(lua_State *L, GCtab *t, uint32_t nasize);
|
||||
|
||||
/* Caveat: all getters except lj_tab_get() can return NULL! */
|
||||
|
||||
LJ_FUNCA cTValue * LJ_FASTCALL lj_tab_getinth(GCtab *t, int32_t key);
|
||||
LJ_FUNC cTValue *lj_tab_getstr(GCtab *t, GCstr *key);
|
||||
LJ_FUNCA cTValue *lj_tab_get(lua_State *L, GCtab *t, cTValue *key);
|
||||
|
||||
/* Caveat: all setters require a write barrier for the stored value. */
|
||||
|
||||
LJ_FUNCA TValue *lj_tab_newkey(lua_State *L, GCtab *t, cTValue *key);
|
||||
LJ_FUNC TValue *lj_tab_setinth(lua_State *L, GCtab *t, int32_t key);
|
||||
LJ_FUNC TValue *lj_tab_setstr(lua_State *L, GCtab *t, GCstr *key);
|
||||
LJ_FUNC TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key);
|
||||
|
||||
#define inarray(t, key) ((MSize)(key) < (MSize)(t)->asize)
|
||||
#define arrayslot(t, i) (&tvref((t)->array)[(i)])
|
||||
#define lj_tab_getint(t, key) \
|
||||
(inarray((t), (key)) ? arrayslot((t), (key)) : lj_tab_getinth((t), (key)))
|
||||
#define lj_tab_setint(L, t, key) \
|
||||
(inarray((t), (key)) ? arrayslot((t), (key)) : lj_tab_setinth(L, (t), (key)))
|
||||
|
||||
LJ_FUNCA int lj_tab_next(lua_State *L, GCtab *t, TValue *key);
|
||||
LJ_FUNCA MSize LJ_FASTCALL lj_tab_len(GCtab *t);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user