diff --git a/README b/README new file mode 100644 index 0000000000..5258c4df27 --- /dev/null +++ b/README @@ -0,0 +1,77 @@ +******************************************** +** THIS IS NOT THE TEST SUITE FOR LUAJIT! ** +******************************************** + +In fact it doesn't even have the steps to build it or run it, +so please don't complain. + +This repo is a place to collect and cleanup tests for LuaJIT. +They should eventually be merged into the main LuaJIT repo. + +It's definitely not in the best state and needs a serious +cleanup effort. Sorry. + + +Many issues need to be resolved before the merge can be performed: + +- Choose a portable test runner + Requirement: very few dependencies, possibly Lua/Shell only + +- Minimal test runner library, wherever assert() is not enough + +- Debugging test failures is a lot simpler, when individual tests can still + be run from the LuaJIT command line without any big dependencies + +- Define consistent grouping of all tests + +- Define consistent naming of all tests + +- Split everything into a lot of tiny tests + +- Reduce time taken to run the test suite + Separate tiers, parallelized testing + +- Some tests can only run under certain configurations (e.g. FFI) + +- Some tests need a clean slate to give reproducible results + Most others should be run from the same state for performance resons + +- Hard to check that the JIT compiler actually generates the intended code + Maybe use a test matching variant of the jit.dump module + +- Portability concerns + +- Avoiding undefined behavior in tests or ignoring it + +- Matrix of architectures + configuration options that need testing + +- Merge tests from other sources, e.g. the various Lua test suites. + +- Tests should go into the LuaJIT git repo, but in separate tarballs + for the releases + + +There are some benchmarks, too: + +- Some of the benchmarks can be used as tests (with low scaling) + by checksumming their output and comparing against known good results + +- Most benchmarks need different scalings to be useful for comparison + on all architectures + + +Note from Mike Pall: + +I've removed all tests of undeterminable origin or that weren't explicitly +contributed with the intention of being part of a public test suite. + +I hereby put all Lua/LuaJIT tests and benchmarks that I wrote under the +public domain. I've removed any copyright headers. + +If I've forgotten an attribution or you want your contributed test to be +removed, please open an issue. + +There are some benchmarks that bear other copyrights, probably public +domain, BSD or MIT licensed. If the status cannot be determined, they +need to be replaced or removed before merging with the LuaJIT repo. + diff --git a/bench/PARAM_arm.txt b/bench/PARAM_arm.txt new file mode 100644 index 0000000000..a07fd010b2 --- /dev/null +++ b/bench/PARAM_arm.txt @@ -0,0 +1,29 @@ +array3d 200 +binary-trees 13 +chameneos 1e6 +coroutine-ring 3e6 +euler14-bit 5e6 +fannkuch 10 +fasta 2e6 +k-nucleotide 5e5 FASTA_500000 +life +mandelbrot 2000 +mandelbrot-bit 2000 +md5 5000 +nbody 1e6 +nsieve 9 +nsieve-bit 9 +nsieve-bit-fp 9 +partialsums 2e6 +pidigits-nogmp 2000 +ray 4 +recursive-ack 9 +recursive-fib 37 +revcomp 1e6 FASTA_1000000 +scimark-fft 2000 +scimark-lu 300 +scimark-sor 5000 +scimark-sparse 5e3 +series 1500 +spectral-norm 1000 +sum-file 1000 SUMCOL_1000 diff --git a/bench/PARAM_mips.txt b/bench/PARAM_mips.txt new file mode 100644 index 0000000000..e6bcadbacd --- /dev/null +++ b/bench/PARAM_mips.txt @@ -0,0 +1,29 @@ +array3d 50 +binary-trees 10 +chameneos 5e4 +coroutine-ring 2e5 +euler14-bit 2e4 +fannkuch 8 +fasta 2e4 +k-nucleotide 1e4 FASTA_10000 +life +mandelbrot 150 +mandelbrot-bit 150 +md5 10 +nbody 1e4 +nsieve 4 +nsieve-bit 4 +nsieve-bit-fp 2 +partialsums 5e4 +pidigits-nogmp 150 +ray 2 +recursive-ack 7 +recursive-fib 29 +revcomp 5e4 FASTA_50000 +scimark-fft 20 +scimark-lu 3 +scimark-sor 40 +scimark-sparse 100 +series 50 +spectral-norm 100 +sum-file 100 SUMCOL_100 diff --git a/bench/PARAM_ppc.txt b/bench/PARAM_ppc.txt new file mode 100644 index 0000000000..c8319a158c --- /dev/null +++ b/bench/PARAM_ppc.txt @@ -0,0 +1,29 @@ +array3d 200 +binary-trees 13 +chameneos 1e6 +coroutine-ring 4e6 +euler14-bit 1e6 +fannkuch 9 +fasta 5e5 +k-nucleotide 1e5 FASTA_100000 +life +mandelbrot 800 +mandelbrot-bit 800 +md5 500 +nbody 1e5 +nsieve 8 +nsieve-bit 8 +nsieve-bit-fp 8 +partialsums 5e5 +pidigits-nogmp 800 +ray 5 +recursive-ack 9 +recursive-fib 34 +revcomp 1e6 FASTA_1000000 +scimark-fft 500 +scimark-lu 100 +scimark-sor 1000 +scimark-sparse 3000 +series 1000 +spectral-norm 200 +sum-file 1000 SUMCOL_1000 diff --git a/bench/PARAM_x86.txt b/bench/PARAM_x86.txt new file mode 100644 index 0000000000..87088d7b13 --- /dev/null +++ b/bench/PARAM_x86.txt @@ -0,0 +1,29 @@ +array3d 300 +binary-trees 16 +chameneos 1e7 +coroutine-ring 2e7 +euler14-bit 2e7 +fannkuch 11 +fasta 25e6 +k-nucleotide 5e6 FASTA_5000000 +life +mandelbrot 5000 +mandelbrot-bit 5000 +md5 20000 +nbody 5e6 +nsieve 12 +nsieve-bit 12 +nsieve-bit-fp 12 +partialsums 1e7 +pidigits-nogmp 5000 +ray 9 +recursive-ack 10 +recursive-fib 40 +revcomp 5e6 FASTA_5000000 +scimark-fft 50000 +scimark-lu 5000 +scimark-sor 50000 +scimark-sparse 15e4 +series 10000 +spectral-norm 3000 +sum-file 5000 SUMCOL_5000 diff --git a/bench/SUMCOL_1.txt b/bench/SUMCOL_1.txt new file mode 100644 index 0000000000..956aba1447 --- /dev/null +++ b/bench/SUMCOL_1.txt @@ -0,0 +1,1000 @@ +276 +498 +-981 +770 +-401 +702 +966 +950 +-853 +-53 +-293 +604 +288 +892 +-697 +204 +96 +408 +880 +-7 +-817 +422 +-261 +-485 +-77 +826 +184 +864 +-751 +626 +812 +-369 +-353 +-371 +488 +-83 +-659 +24 +524 +-21 +840 +-757 +-17 +-973 +-843 +260 +858 +-389 +-521 +-99 +482 +-561 +-213 +630 +766 +932 +112 +-419 +-877 +762 +266 +-837 +170 +834 +746 +764 +922 +-89 +576 +-63 +90 +684 +316 +506 +-959 +708 +70 +252 +-747 +342 +-593 +-895 +-937 +-707 +350 +588 +-201 +-683 +-113 +-511 +-867 +322 +202 +472 +150 +-9 +-643 +28 +336 +86 +-925 +836 +-473 +-451 +-971 +-805 +-619 +84 +-67 +806 +270 +366 +334 +-555 +-557 +-331 +-409 +-553 +-145 +-71 +528 +490 +492 +828 +628 +-961 +536 +-859 +-271 +974 +-671 +-749 +414 +-257 +778 +56 +598 +-437 +-899 +-785 +-987 +32 +-999 +132 +-821 +-209 +402 +-543 +194 +-967 +294 +-943 +-285 +-483 +-97 +660 +-481 +-829 +-309 +-597 +-855 +80 +-355 +192 +-823 +436 +916 +282 +-629 +612 +-329 +-535 +780 +-47 +706 +110 +756 +-857 +-933 +-345 +-523 +718 +-31 +902 +678 +540 +698 +456 +-399 +126 +412 +-563 +-321 +-487 +-641 +-195 +-199 +-955 +772 +570 +18 +-217 +886 +984 +-721 +-995 +46 +-989 +946 +64 +716 +-719 +-869 +-579 +776 +450 +936 +980 +-439 +-977 +-455 +-997 +6 +268 +-269 +-421 +328 +352 +578 +-575 +476 +976 +-57 +-469 +544 +582 +-43 +510 +-939 +-581 +-337 +-203 +-737 +-827 +852 +-279 +-803 +-911 +-865 +548 +48 +-75 +416 +-275 +688 +-255 +-687 +-461 +-233 +420 +912 +-901 +-299 +12 +568 +694 +-411 +-883 +-327 +-361 +-339 +646 +-137 +-905 +670 +686 +-131 +-849 +-825 +256 +228 +-841 +68 +368 +-909 +242 +298 +118 +10 +222 +954 +-493 +-459 +-445 +608 +-765 +34 +468 +-715 +690 +-185 +-551 +-571 +-241 +292 +92 +768 +-923 +956 +614 +8 +730 +208 +-417 +300 +136 +-59 +-251 +-539 +166 +798 +866 +454 +-391 +-317 +668 +502 +-15 +994 +854 +-189 +666 +446 +-565 +-5 +42 +-227 +-87 +-779 +26 +312 +354 +754 +396 +-515 +220 +872 +654 +88 +-667 +250 +572 +952 +72 +982 +972 +-529 +-471 +-533 +-427 +538 +154 +-457 +-819 +750 +152 +452 +-41 +838 +-489 +418 +-649 +-637 +-197 +74 +394 +-653 +-727 +-435 +-23 +348 +638 +-611 +914 +-357 +-743 +-685 +580 +-247 +-577 +54 +-931 +-3 +558 +-793 +-443 +-759 +162 +-811 +384 +720 +-117 +900 +-519 +-39 +744 +432 +286 +-873 +380 +-167 +-283 +430 +-155 +-755 +206 +100 +364 +-677 +332 +-567 +382 +-605 +-181 +676 +-475 +-845 +910 +546 +14 +398 +616 +-769 +424 +992 +-235 +-239 +774 +478 +-919 +168 +-771 +-773 +-69 +-509 +930 +550 +-463 +178 +-861 +-761 +-795 +234 +-831 +-61 +-979 +-851 +-665 +-709 +896 +742 +-123 +590 +-693 +-887 +-379 +144 +-717 +20 +174 +82 +464 +30 +-969 +-349 +-531 +-799 +-661 +-647 +-623 +878 +148 +-545 +238 +-259 +554 +726 +-37 +-797 +98 +78 +-591 +-975 +962 +120 +906 +-207 +656 +-171 +652 +188 +672 +-133 +-91 +224 +818 +-333 +-839 +-499 +22 +-739 +142 +378 +-403 +-315 +370 +284 +122 +230 +-527 +-127 +442 +534 +160 +722 +262 +-657 +304 +258 +-103 +960 +-495 +-265 +634 +-101 +480 +-363 +308 +76 +-949 +-585 +904 +146 +-703 +164 +850 +246 +732 +-725 +566 +274 +-163 +-935 +-681 +-229 +254 +-733 +-547 +-273 +-903 +736 +-711 +794 +392 +-655 +-549 +808 +-429 +484 +-701 +-617 +804 +36 +-775 +-335 +-927 +714 +-177 +-325 +-413 +-963 +114 +-253 +-789 +-645 +40 +434 +898 +924 +-19 +738 +788 +280 +-121 +594 +-913 +426 +816 +-373 +-45 +340 +-109 +-323 +58 +-249 +940 +-297 +988 +998 +-607 +-745 +-633 +-115 +996 +-893 +696 +400 +848 +500 +-263 +562 +-807 +-105 +-603 +658 +-73 +-863 +448 +680 +-157 +-161 +728 +814 +-477 +-375 +1000 +-631 +-991 +362 +156 +-187 +-705 +-917 +-449 +-741 +556 +440 +-589 +-11 +-359 +-891 +-801 +-153 +-381 +938 +-173 +-243 +618 +-599 +-497 +486 +128 +790 +460 +-27 +-305 +-205 +-215 +324 +-341 +50 +458 +52 +-621 +874 +386 +560 +-569 +-51 +802 +786 +920 +-425 +466 +444 +-507 +-915 +346 +622 +-679 +784 +-689 +388 +508 +-613 +-313 +-447 +564 +-897 +-211 +-225 +-615 +-367 +186 +894 +-65 +-453 +-245 +602 +496 +-651 +-601 +820 +226 +-695 +-119 +372 +180 +94 +214 +542 +648 +-871 +592 +584 +824 +796 +374 +-945 +-311 +516 +942 +-221 +-433 +200 +-465 +-953 +870 +868 +-879 +518 +356 +-223 +682 +990 +-191 +-541 +-951 +-921 +-319 +-169 +-291 +-289 +792 +876 +306 +-491 +326 +-885 +62 +514 +-929 +318 +-231 +632 +44 +-107 +644 +-267 +-343 +-847 +934 +734 +-505 +-351 +574 +-627 +636 +-93 +-431 +-835 +428 +-183 +-151 +2 +-813 +-595 +958 +-141 +692 +-385 +610 +-179 +376 +948 +198 +-675 +964 +-907 +918 +-165 +-1 +406 +748 +-111 +532 +-55 +-281 +740 +504 +236 +-29 +662 +-713 +-537 +196 +-587 +822 +-135 +700 +-35 +674 +-407 +240 +-673 +-669 +-393 +470 +-525 +-875 +-383 +-625 +296 +-85 +-147 +-277 +800 +-691 +-143 +16 +-983 +-303 +290 +-139 +172 +320 +512 +596 +640 +664 +-791 +-783 +-387 +-735 +-467 +-301 +810 +134 +216 +278 +176 +606 +140 +-787 +978 +586 +890 +882 +-753 +-13 +970 +-941 +-175 +-777 +-809 +-441 +-347 +-377 +390 +-423 +842 +642 +190 +302 +438 +704 +310 +-49 +124 +-781 +-287 +724 +-767 +830 +620 +-295 +244 +-159 +-307 +-397 +66 +-237 +314 +-79 +624 +710 +272 +-365 +928 +856 +138 +-479 +520 +832 +862 +760 +846 +-81 +106 +-513 +-193 +650 +782 +-517 +944 +218 +712 +-663 +-559 +462 +-635 +-25 +182 +530 +844 +330 +-833 +102 +-881 +108 +-947 +-763 +-405 +232 +410 +104 +-729 +-149 +-889 +888 +360 +968 +908 +116 +-815 +-129 +522 +-723 +-993 +860 +-503 +926 +-219 +-415 +60 +158 +-609 +-501 +986 +-699 +-583 +884 +212 +210 +-957 +526 +-985 +552 +344 +-395 +-95 +338 +248 +494 +130 +404 +358 +600 +-639 +-125 +-33 +-965 +752 +474 +-731 +758 +-573 +4 +38 +264 diff --git a/bench/TEST_md5sum.txt b/bench/TEST_md5sum.txt new file mode 100644 index 0000000000..15aa8a1c8f --- /dev/null +++ b/bench/TEST_md5sum.txt @@ -0,0 +1,20 @@ +binarytrees 10 7202f4e13df7abc5ad8c07f05fe9d644 +chameneos 1e5 a629ce12f63050c6656bce175258cf8f +cheapconcr 1000 d29799d1e263810a4db7bbf43ca66499 +cheapconcw 1000 d29799d1e263810a4db7bbf43ca66499 +fannkuch 8 51e5e372cbc5471ea8940b20ad782319 +fasta 1e5 78cd327de6f0a5667da0aa9349888279 +knucleotide x 88efb24c1fed533959ed84bb32c88142 = 0 and x < self.nx, "x outside PA") + assert(y >= 0 and y < self.ny, "y outside PA") + assert(z >= 0 and z < self.nz, "z outside PA") + local pos = (z*self.ny + y)*self.nx + x + local image = self.image + if self.packed then + local maxv = self.max_voltage + if p > maxv then self.max_voltage = p*2.0 end + local oldp = image[pos] or 0.0 -- Works with uninitialized table, too + if oldp > maxv then p = p + maxv*2.0 end + image[pos] = p + else + image[pos] = p + end + self.changed = true + self.changed_recently = true +end + +local function array_points(self) + local y, z = 0, 0 + return function(self, x) + x = x + 1 + if x >= self.nx then + x = 0 + y = y + 1 + if y >= self.ny then + y = 0 + z = z + 1 + if z >= self.nz then + return nil, nil, nil + end + end + end + return x, y, z + end, self, 0 +end + +local function array_new(nx, ny, nz, packed) + return { + nx = nx, ny = ny, nz = nz, + packed = packed, max_voltage = 0.0, + changed = false, changed_recently = false, + image = {}, -- Preferably use a fixed-type, pre-sized array here. + set = array_set, + points = array_points, + } +end + +local dim = tonumber(arg and arg[1]) or 300 -- Array dimension dim^3 +local packed = arg and arg[2] == "packed" -- Packed image or flat +local arr = array_new(dim, dim, dim, packed) + +for x,y,z in arr:points() do + arr:set(x, y, z, x*x) +end +assert(arr.image[dim^3-1] == (dim-1)^2) + diff --git a/bench/binary-trees.lua b/bench/binary-trees.lua new file mode 100644 index 0000000000..bf0404666f --- /dev/null +++ b/bench/binary-trees.lua @@ -0,0 +1,47 @@ + +local function BottomUpTree(item, depth) + if depth > 0 then + local i = item + item + depth = depth - 1 + local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth) + return { item, left, right } + else + return { item } + end +end + +local function ItemCheck(tree) + if tree[2] then + return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3]) + else + return tree[1] + end +end + +local N = tonumber(arg and arg[1]) or 0 +local mindepth = 4 +local maxdepth = mindepth + 2 +if maxdepth < N then maxdepth = N end + +do + local stretchdepth = maxdepth + 1 + local stretchtree = BottomUpTree(0, stretchdepth) + io.write(string.format("stretch tree of depth %d\t check: %d\n", + stretchdepth, ItemCheck(stretchtree))) +end + +local longlivedtree = BottomUpTree(0, maxdepth) + +for depth=mindepth,maxdepth,2 do + local iterations = 2 ^ (maxdepth - depth + mindepth) + local check = 0 + for i=1,iterations do + check = check + ItemCheck(BottomUpTree(1, depth)) + + ItemCheck(BottomUpTree(-1, depth)) + end + io.write(string.format("%d\t trees of depth %d\t check: %d\n", + iterations*2, depth, check)) +end + +io.write(string.format("long lived tree of depth %d\t check: %d\n", + maxdepth, ItemCheck(longlivedtree))) diff --git a/bench/chameneos.lua b/bench/chameneos.lua new file mode 100644 index 0000000000..78b64c3f3b --- /dev/null +++ b/bench/chameneos.lua @@ -0,0 +1,68 @@ + +local co = coroutine +local create, resume, yield = co.create, co.resume, co.yield + +local N = tonumber(arg and arg[1]) or 10 +local first, second + +-- Meet another creature. +local function meet(me) + while second do yield() end -- Wait until meeting place clears. + local other = first + if other then -- Hey, I found a new friend! + first = nil + second = me + else -- Sniff, nobody here (yet). + local n = N - 1 + if n < 0 then return end -- Uh oh, the mall is closed. + N = n + first = me + repeat yield(); other = second until other -- Wait for another creature. + second = nil + yield() -- Be nice and let others meet up. + end + return other +end + +-- Create a very social creature. +local function creature(color) + return create(function() + local me = color + for met=0,1000000000 do + local other = meet(me) + if not other then return met end + if me ~= other then + if me == "blue" then me = other == "red" and "yellow" or "red" + elseif me == "red" then me = other == "blue" and "yellow" or "blue" + else me = other == "blue" and "red" or "blue" end + end + end + end) +end + +-- Trivial round-robin scheduler. +local function schedule(threads) + local resume = resume + local nthreads, meetings = #threads, 0 + repeat + for i=1,nthreads do + local thr = threads[i] + if not thr then return meetings end + local ok, met = resume(thr) + if met then + meetings = meetings + met + threads[i] = nil + end + end + until false +end + +-- A bunch of colorful creatures. +local threads = { + creature("blue"), + creature("red"), + creature("yellow"), + creature("blue"), +} + +io.write(schedule(threads), "\n") diff --git a/bench/coroutine-ring.lua b/bench/coroutine-ring.lua new file mode 100644 index 0000000000..1e8c5ef688 --- /dev/null +++ b/bench/coroutine-ring.lua @@ -0,0 +1,42 @@ +-- The Computer Language Benchmarks Game +-- http://shootout.alioth.debian.org/ +-- contributed by Sam Roberts +-- reviewed by Bruno Massa + +local n = tonumber(arg and arg[1]) or 2e7 + +-- fixed size pool +local poolsize = 503 +local threads = {} + +-- cache these to avoid global environment lookups +local create = coroutine.create +local resume = coroutine.resume +local yield = coroutine.yield + +local id = 1 +local token = 0 +local ok + +local body = function(token) + while true do + token = yield(token + 1) + end +end + +-- create all threads +for id = 1, poolsize do + threads[id] = create(body) +end + +-- send the token +repeat + if id == poolsize then + id = 1 + else + id = id + 1 + end + ok, token = resume(threads[id], token) +until token == n + +io.write(id, "\n") diff --git a/bench/euler14-bit.lua b/bench/euler14-bit.lua new file mode 100644 index 0000000000..537f2bf322 --- /dev/null +++ b/bench/euler14-bit.lua @@ -0,0 +1,22 @@ + +local bit = require("bit") +local bnot, bor, band = bit.bnot, bit.bor, bit.band +local shl, shr = bit.lshift, bit.rshift + +local N = tonumber(arg and arg[1]) or 10000000 +local cache, m, n = { 1 }, 1, 1 +if arg and arg[2] then cache = nil end +for i=2,N do + local j = i + for len=1,1000000000 do + j = bor(band(shr(j,1), band(j,1)-1), band(shl(j,1)+j+1, bnot(band(j,1)-1))) + if cache then + local x = cache[j]; if x then j = x+len; break end + elseif j == 1 then + j = len+1; break + end + end + if cache then cache[i] = j end + if j > m then m, n = j, i end +end +io.write("Found ", n, " (chain length: ", m, ")\n") diff --git a/bench/fannkuch.lua b/bench/fannkuch.lua new file mode 100644 index 0000000000..2a4cd4267a --- /dev/null +++ b/bench/fannkuch.lua @@ -0,0 +1,50 @@ + +local function fannkuch(n) + local p, q, s, odd, check, maxflips = {}, {}, {}, true, 0, 0 + for i=1,n do p[i] = i; q[i] = i; s[i] = i end + repeat + -- Print max. 30 permutations. + if check < 30 then + if not p[n] then return maxflips end -- Catch n = 0, 1, 2. + io.write(unpack(p)); io.write("\n") + check = check + 1 + end + -- Copy and flip. + local q1 = p[1] -- Cache 1st element. + if p[n] ~= n and q1 ~= 1 then -- Avoid useless work. + for i=2,n do q[i] = p[i] end -- Work on a copy. + local flips = 1 -- Flip ... + while true do + local qq = q[q1] + if qq == 1 then -- ... until 1st element is 1. + if flips > maxflips then maxflips = flips end -- New maximum? + break + end + q[q1] = q1 + if q1 >= 4 then + local i, j = 2, q1 - 1 + repeat q[i], q[j] = q[j], q[i]; i = i + 1; j = j - 1; until i >= j + end + q1 = qq + flips=flips+1 + end + end + -- Permute. + if odd then + p[2], p[1] = p[1], p[2]; odd = false -- Rotate 1<-2. + else + p[2], p[3] = p[3], p[2]; odd = true -- Rotate 1<-2 and 1<-2<-3. + for i=3,n do + local sx = s[i] + if sx ~= 1 then s[i] = sx-1; break end + if i == n then return maxflips end -- Out of permutations. + s[i] = i + -- Rotate 1<-...<-i+1. + local t=p[1]; for j=i+1,1,-1 do p[j],t=t,p[j] end + end + end + until false +end + +local n = tonumber(arg and arg[1]) or 1 +io.write("Pfannkuchen(", n, ") = ", fannkuch(n), "\n") diff --git a/bench/fasta.lua b/bench/fasta.lua new file mode 100644 index 0000000000..7ce6080411 --- /dev/null +++ b/bench/fasta.lua @@ -0,0 +1,95 @@ + +local Last = 42 +local function random(max) + local y = (Last * 3877 + 29573) % 139968 + Last = y + return (max * y) / 139968 +end + +local function make_repeat_fasta(id, desc, s, n) + local write, sub = io.write, string.sub + write(">", id, " ", desc, "\n") + local p, sn, s2 = 1, #s, s..s + for i=60,n,60 do + write(sub(s2, p, p + 59), "\n") + p = p + 60; if p > sn then p = p - sn end + end + local tail = n % 60 + if tail > 0 then write(sub(s2, p, p + tail-1), "\n") end +end + +local function make_random_fasta(id, desc, bs, n) + io.write(">", id, " ", desc, "\n") + loadstring([=[ + local write, char, unpack, n, random = io.write, string.char, unpack, ... + local buf, p = {}, 1 + for i=60,n,60 do + for j=p,p+59 do ]=]..bs..[=[ end + buf[p+60] = 10; p = p + 61 + if p >= 2048 then write(char(unpack(buf, 1, p-1))); p = 1 end + end + local tail = n % 60 + if tail > 0 then + for j=p,p+tail-1 do ]=]..bs..[=[ end + p = p + tail; buf[p] = 10; p = p + 1 + end + write(char(unpack(buf, 1, p-1))) + ]=], desc)(n, random) +end + +local function bisect(c, p, lo, hi) + local n = hi - lo + if n == 0 then return "buf[j] = "..c[hi].."\n" end + local mid = math.floor(n / 2) + return "if r < "..p[lo+mid].." then\n"..bisect(c, p, lo, lo+mid).. + "else\n"..bisect(c, p, lo+mid+1, hi).."end\n" +end + +local function make_bisect(tab) + local c, p, sum = {}, {}, 0 + for i,row in ipairs(tab) do + c[i] = string.byte(row[1]) + sum = sum + row[2] + p[i] = sum + end + return "local r = random(1)\n"..bisect(c, p, 1, #tab) +end + +local alu = + "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG".. + "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA".. + "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT".. + "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA".. + "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG".. + "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC".. + "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA" + +local iub = make_bisect{ + { "a", 0.27 }, + { "c", 0.12 }, + { "g", 0.12 }, + { "t", 0.27 }, + { "B", 0.02 }, + { "D", 0.02 }, + { "H", 0.02 }, + { "K", 0.02 }, + { "M", 0.02 }, + { "N", 0.02 }, + { "R", 0.02 }, + { "S", 0.02 }, + { "V", 0.02 }, + { "W", 0.02 }, + { "Y", 0.02 }, +} + +local homosapiens = make_bisect{ + { "a", 0.3029549426680 }, + { "c", 0.1979883004921 }, + { "g", 0.1975473066391 }, + { "t", 0.3015094502008 }, +} + +local N = tonumber(arg and arg[1]) or 1000 +make_repeat_fasta('ONE', 'Homo sapiens alu', alu, N*2) +make_random_fasta('TWO', 'IUB ambiguity codes', iub, N*3) +make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, N*5) diff --git a/bench/k-nucleotide.lua b/bench/k-nucleotide.lua new file mode 100644 index 0000000000..0bfb41bec5 --- /dev/null +++ b/bench/k-nucleotide.lua @@ -0,0 +1,58 @@ + +local function kfrequency(seq, freq, k, frame) + local sub = string.sub + local k1 = k - 1 + for i=frame,#seq-k1,k do + local c = sub(seq, i, i+k1) + freq[c] = (freq[c] or 0) + 1 + end +end + +local function count(seq, frag) + local k = #frag + local freq = {} + for frame=1,k do kfrequency(seq, freq, k, frame) end + io.write(freq[frag] or 0, "\t", frag, "\n") +end + +local function frequency(seq, k) + local freq = {} + for frame=1,k do kfrequency(seq, freq, k, frame) end + local sfreq, sn, sum = {}, 1, 0 + for c,v in pairs(freq) do sfreq[sn] = c; sn = sn + 1; sum = sum + v end + table.sort(sfreq, function(a, b) + local fa, fb = freq[a], freq[b] + return fa == fb and a > b or fa > fb + end) + for _,c in ipairs(sfreq) do + io.write(string.format("%s %0.3f\n", c, (freq[c]*100)/sum)) + end + io.write("\n") +end + +local function readseq() + local sub = string.sub + for line in io.lines() do + if sub(line, 1, 1) == ">" and sub(line, 2, 6) == "THREE" then break end + end + local lines, ln = {}, 0 + for line in io.lines() do + local c = sub(line, 1, 1) + if c == ">" then + break + elseif c ~= ";" then + ln = ln + 1 + lines[ln] = line + end + end + return string.upper(table.concat(lines, "", 1, ln)) +end + +local seq = readseq() +frequency(seq, 1) +frequency(seq, 2) +count(seq, "GGT") +count(seq, "GGTA") +count(seq, "GGTATT") +count(seq, "GGTATTTTAATT") +count(seq, "GGTATTTTAATTTATAGT") diff --git a/bench/life.lua b/bench/life.lua new file mode 100644 index 0000000000..911d9fe177 --- /dev/null +++ b/bench/life.lua @@ -0,0 +1,111 @@ +-- life.lua +-- original by Dave Bollinger posted to lua-l +-- modified to use ANSI terminal escape sequences +-- modified to use for instead of while + +local write=io.write + +ALIVE="¥" DEAD="þ" +ALIVE="O" DEAD="-" + +function delay() -- NOTE: SYSTEM-DEPENDENT, adjust as necessary + for i=1,10000 do end + -- local i=os.clock()+1 while(os.clock() 0 do + local xm1,x,xp1,xi=self.w-1,self.w,1,self.w + while xi > 0 do + local sum = self[ym1][xm1] + self[ym1][x] + self[ym1][xp1] + + self[y][xm1] + self[y][xp1] + + self[yp1][xm1] + self[yp1][x] + self[yp1][xp1] + next[y][x] = ((sum==2) and self[y][x]) or ((sum==3) and 1) or 0 + xm1,x,xp1,xi = x,xp1,xp1+1,xi-1 + end + ym1,y,yp1,yi = y,yp1,yp1+1,yi-1 + end +end + +-- output the array to screen +function _CELLS:draw() + local out="" -- accumulate to reduce flicker + for y=1,self.h do + for x=1,self.w do + out=out..(((self[y][x]>0) and ALIVE) or DEAD) + end + out=out.."\n" + end + write(out) +end + +-- constructor +function CELLS(w,h) + local c = ARRAY2D(w,h) + c.spawn = _CELLS.spawn + c.evolve = _CELLS.evolve + c.draw = _CELLS.draw + return c +end + +-- +-- shapes suitable for use with spawn() above +-- +HEART = { 1,0,1,1,0,1,1,1,1; w=3,h=3 } +GLIDER = { 0,0,1,1,0,1,0,1,1; w=3,h=3 } +EXPLODE = { 0,1,0,1,1,1,1,0,1,0,1,0; w=3,h=4 } +FISH = { 0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,1,0,0,1,0; w=5,h=4 } +BUTTERFLY = { 1,0,0,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,1; w=5,h=5 } + +-- the main routine +function LIFE(w,h) + -- create two arrays + local thisgen = CELLS(w,h) + local nextgen = CELLS(w,h) + + -- create some life + -- about 1000 generations of fun, then a glider steady-state + thisgen:spawn(GLIDER,5,4) + thisgen:spawn(EXPLODE,25,10) + thisgen:spawn(FISH,4,12) + + -- run until break + local gen=1 + write("\027[2J") -- ANSI clear screen + while 1 do + thisgen:evolve(nextgen) + thisgen,nextgen = nextgen,thisgen + write("\027[H") -- ANSI home cursor + thisgen:draw() + write("Life - generation ",gen,"\n") + gen=gen+1 + if gen>2000 then break end + --delay() -- no delay + end +end + +LIFE(40,20) diff --git a/bench/mandelbrot-bit.lua b/bench/mandelbrot-bit.lua new file mode 100644 index 0000000000..91d96975c8 --- /dev/null +++ b/bench/mandelbrot-bit.lua @@ -0,0 +1,33 @@ + +local bit = require("bit") +local bor, band = bit.bor, bit.band +local shl, shr, rol = bit.lshift, bit.rshift, bit.rol +local write, char, unpack = io.write, string.char, unpack +local N = tonumber(arg and arg[1]) or 100 +local M, buf = 2/N, {} +write("P4\n", N, " ", N, "\n") +for y=0,N-1 do + local Ci, b, p = y*M-1, -16777216, 0 + local Ciq = Ci*Ci + for x=0,N-1,2 do + local Cr, Cr2 = x*M-1.5, (x+1)*M-1.5 + local Zr, Zi, Zrq, Ziq = Cr, Ci, Cr*Cr, Ciq + local Zr2, Zi2, Zrq2, Ziq2 = Cr2, Ci, Cr2*Cr2, Ciq + b = rol(b, 2) + for i=1,49 do + Zi = Zr*Zi*2 + Ci; Zi2 = Zr2*Zi2*2 + Ci + Zr = Zrq-Ziq + Cr; Zr2 = Zrq2-Ziq2 + Cr2 + Ziq = Zi*Zi; Ziq2 = Zi2*Zi2 + Zrq = Zr*Zr; Zrq2 = Zr2*Zr2 + if band(b, 2) ~= 0 and Zrq+Ziq > 4.0 then b = band(b, -3) end + if band(b, 1) ~= 0 and Zrq2+Ziq2 > 4.0 then b = band(b, -2) end + if band(b, 3) == 0 then break end + end + if b >= 0 then p = p + 1; buf[p] = b; b = -16777216; end + end + if b ~= -16777216 then + if band(N, 1) ~= 0 then b = shr(b, 1) end + p = p + 1; buf[p] = shl(b, 8-band(N, 7)) + end + write(char(unpack(buf, 1, p))) +end diff --git a/bench/mandelbrot.lua b/bench/mandelbrot.lua new file mode 100644 index 0000000000..0ef595a2fe --- /dev/null +++ b/bench/mandelbrot.lua @@ -0,0 +1,23 @@ + +local write, char, unpack = io.write, string.char, unpack +local N = tonumber(arg and arg[1]) or 100 +local M, ba, bb, buf = 2/N, 2^(N%8+1)-1, 2^(8-N%8), {} +write("P4\n", N, " ", N, "\n") +for y=0,N-1 do + local Ci, b, p = y*M-1, 1, 0 + for x=0,N-1 do + local Cr = x*M-1.5 + local Zr, Zi, Zrq, Ziq = Cr, Ci, Cr*Cr, Ci*Ci + b = b + b + for i=1,49 do + Zi = Zr*Zi*2 + Ci + Zr = Zrq-Ziq + Cr + Ziq = Zi*Zi + Zrq = Zr*Zr + if Zrq+Ziq > 4.0 then b = b + 1; break; end + end + if b >= 256 then p = p + 1; buf[p] = 511 - b; b = 1; end + end + if b ~= 1 then p = p + 1; buf[p] = (ba-b)*bb; end + write(char(unpack(buf, 1, p))) +end diff --git a/bench/md5.lua b/bench/md5.lua new file mode 100644 index 0000000000..fdf6b4a7c7 --- /dev/null +++ b/bench/md5.lua @@ -0,0 +1,183 @@ + +local bit = require("bit") +local tobit, tohex, bnot = bit.tobit or bit.cast, bit.tohex, bit.bnot +local bor, band, bxor = bit.bor, bit.band, bit.bxor +local lshift, rshift, rol, bswap = bit.lshift, bit.rshift, bit.rol, bit.bswap +local byte, char, sub, rep = string.byte, string.char, string.sub, string.rep + +if not rol then -- Replacement function if rotates are missing. + local bor, shl, shr = bit.bor, bit.lshift, bit.rshift + function rol(a, b) return bor(shl(a, b), shr(a, 32-b)) end +end + +if not bswap then -- Replacement function if bswap is missing. + local bor, band, shl, shr = bit.bor, bit.band, bit.lshift, bit.rshift + function bswap(a) + return bor(shr(a, 24), band(shr(a, 8), 0xff00), + shl(band(a, 0xff00), 8), shl(a, 24)); + end +end + +if not tohex then -- (Unreliable) replacement function if tohex is missing. + function tohex(a) + return string.sub(string.format("%08x", a), -8) + end +end + +local function tr_f(a, b, c, d, x, s) + return rol(bxor(d, band(b, bxor(c, d))) + a + x, s) + b +end + +local function tr_g(a, b, c, d, x, s) + return rol(bxor(c, band(d, bxor(b, c))) + a + x, s) + b +end + +local function tr_h(a, b, c, d, x, s) + return rol(bxor(b, c, d) + a + x, s) + b +end + +local function tr_i(a, b, c, d, x, s) + return rol(bxor(c, bor(b, bnot(d))) + a + x, s) + b +end + +local function transform(x, a1, b1, c1, d1) + local a, b, c, d = a1, b1, c1, d1 + + a = tr_f(a, b, c, d, x[ 1] + 0xd76aa478, 7) + d = tr_f(d, a, b, c, x[ 2] + 0xe8c7b756, 12) + c = tr_f(c, d, a, b, x[ 3] + 0x242070db, 17) + b = tr_f(b, c, d, a, x[ 4] + 0xc1bdceee, 22) + a = tr_f(a, b, c, d, x[ 5] + 0xf57c0faf, 7) + d = tr_f(d, a, b, c, x[ 6] + 0x4787c62a, 12) + c = tr_f(c, d, a, b, x[ 7] + 0xa8304613, 17) + b = tr_f(b, c, d, a, x[ 8] + 0xfd469501, 22) + a = tr_f(a, b, c, d, x[ 9] + 0x698098d8, 7) + d = tr_f(d, a, b, c, x[10] + 0x8b44f7af, 12) + c = tr_f(c, d, a, b, x[11] + 0xffff5bb1, 17) + b = tr_f(b, c, d, a, x[12] + 0x895cd7be, 22) + a = tr_f(a, b, c, d, x[13] + 0x6b901122, 7) + d = tr_f(d, a, b, c, x[14] + 0xfd987193, 12) + c = tr_f(c, d, a, b, x[15] + 0xa679438e, 17) + b = tr_f(b, c, d, a, x[16] + 0x49b40821, 22) + + a = tr_g(a, b, c, d, x[ 2] + 0xf61e2562, 5) + d = tr_g(d, a, b, c, x[ 7] + 0xc040b340, 9) + c = tr_g(c, d, a, b, x[12] + 0x265e5a51, 14) + b = tr_g(b, c, d, a, x[ 1] + 0xe9b6c7aa, 20) + a = tr_g(a, b, c, d, x[ 6] + 0xd62f105d, 5) + d = tr_g(d, a, b, c, x[11] + 0x02441453, 9) + c = tr_g(c, d, a, b, x[16] + 0xd8a1e681, 14) + b = tr_g(b, c, d, a, x[ 5] + 0xe7d3fbc8, 20) + a = tr_g(a, b, c, d, x[10] + 0x21e1cde6, 5) + d = tr_g(d, a, b, c, x[15] + 0xc33707d6, 9) + c = tr_g(c, d, a, b, x[ 4] + 0xf4d50d87, 14) + b = tr_g(b, c, d, a, x[ 9] + 0x455a14ed, 20) + a = tr_g(a, b, c, d, x[14] + 0xa9e3e905, 5) + d = tr_g(d, a, b, c, x[ 3] + 0xfcefa3f8, 9) + c = tr_g(c, d, a, b, x[ 8] + 0x676f02d9, 14) + b = tr_g(b, c, d, a, x[13] + 0x8d2a4c8a, 20) + + a = tr_h(a, b, c, d, x[ 6] + 0xfffa3942, 4) + d = tr_h(d, a, b, c, x[ 9] + 0x8771f681, 11) + c = tr_h(c, d, a, b, x[12] + 0x6d9d6122, 16) + b = tr_h(b, c, d, a, x[15] + 0xfde5380c, 23) + a = tr_h(a, b, c, d, x[ 2] + 0xa4beea44, 4) + d = tr_h(d, a, b, c, x[ 5] + 0x4bdecfa9, 11) + c = tr_h(c, d, a, b, x[ 8] + 0xf6bb4b60, 16) + b = tr_h(b, c, d, a, x[11] + 0xbebfbc70, 23) + a = tr_h(a, b, c, d, x[14] + 0x289b7ec6, 4) + d = tr_h(d, a, b, c, x[ 1] + 0xeaa127fa, 11) + c = tr_h(c, d, a, b, x[ 4] + 0xd4ef3085, 16) + b = tr_h(b, c, d, a, x[ 7] + 0x04881d05, 23) + a = tr_h(a, b, c, d, x[10] + 0xd9d4d039, 4) + d = tr_h(d, a, b, c, x[13] + 0xe6db99e5, 11) + c = tr_h(c, d, a, b, x[16] + 0x1fa27cf8, 16) + b = tr_h(b, c, d, a, x[ 3] + 0xc4ac5665, 23) + + a = tr_i(a, b, c, d, x[ 1] + 0xf4292244, 6) + d = tr_i(d, a, b, c, x[ 8] + 0x432aff97, 10) + c = tr_i(c, d, a, b, x[15] + 0xab9423a7, 15) + b = tr_i(b, c, d, a, x[ 6] + 0xfc93a039, 21) + a = tr_i(a, b, c, d, x[13] + 0x655b59c3, 6) + d = tr_i(d, a, b, c, x[ 4] + 0x8f0ccc92, 10) + c = tr_i(c, d, a, b, x[11] + 0xffeff47d, 15) + b = tr_i(b, c, d, a, x[ 2] + 0x85845dd1, 21) + a = tr_i(a, b, c, d, x[ 9] + 0x6fa87e4f, 6) + d = tr_i(d, a, b, c, x[16] + 0xfe2ce6e0, 10) + c = tr_i(c, d, a, b, x[ 7] + 0xa3014314, 15) + b = tr_i(b, c, d, a, x[14] + 0x4e0811a1, 21) + a = tr_i(a, b, c, d, x[ 5] + 0xf7537e82, 6) + d = tr_i(d, a, b, c, x[12] + 0xbd3af235, 10) + c = tr_i(c, d, a, b, x[ 3] + 0x2ad7d2bb, 15) + b = tr_i(b, c, d, a, x[10] + 0xeb86d391, 21) + + return tobit(a+a1), tobit(b+b1), tobit(c+c1), tobit(d+d1) +end + +-- Note: this is copying the original string and NOT particularly fast. +-- A library for struct unpacking would make this task much easier. +local function md5(msg) + local len = #msg + msg = msg.."\128"..rep("\0", 63 - band(len + 8, 63)) + ..char(band(lshift(len, 3), 255), band(rshift(len, 5), 255), + band(rshift(len, 13), 255), band(rshift(len, 21), 255)) + .."\0\0\0\0" + local a, b, c, d = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 + local x, k = {}, 1 + for i=1,#msg,4 do + local m0, m1, m2, m3 = byte(msg, i, i+3) + x[k] = bor(m0, lshift(m1, 8), lshift(m2, 16), lshift(m3, 24)) + if k == 16 then + a, b, c, d = transform(x, a, b, c, d) + k = 1 + else + k = k + 1 + end + end + return tohex(bswap(a))..tohex(bswap(b))..tohex(bswap(c))..tohex(bswap(d)) +end + +assert(md5('') == 'd41d8cd98f00b204e9800998ecf8427e') +assert(md5('a') == '0cc175b9c0f1b6a831c399e269772661') +assert(md5('abc') == '900150983cd24fb0d6963f7d28e17f72') +assert(md5('message digest') == 'f96b697d7cb7938d525a2f31aaf161d0') +assert(md5('abcdefghijklmnopqrstuvwxyz') == 'c3fcd3d76192e4007dfb496cca67e13b') +assert(md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') == + 'd174ab98d277d9f5a5611c2c9f419d9f') +assert(md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890') == + '57edf4a22be3c955ac49da2e2107b67a') + +local N = tonumber(arg and arg[1]) or 10000 + + -- Credits: William Shakespeare, Romeo and Juliet +local txt = [[Rebellious subjects, enemies to peace, +Profaners of this neighbour-stained steel,-- +Will they not hear? What, ho! you men, you beasts, +That quench the fire of your pernicious rage +With purple fountains issuing from your veins, +On pain of torture, from those bloody hands +Throw your mistemper'd weapons to the ground, +And hear the sentence of your moved prince. +Three civil brawls, bred of an airy word, +By thee, old Capulet, and Montague, +Have thrice disturb'd the quiet of our streets, +And made Verona's ancient citizens +Cast by their grave beseeming ornaments, +To wield old partisans, in hands as old, +Canker'd with peace, to part your canker'd hate: +If ever you disturb our streets again, +Your lives shall pay the forfeit of the peace. +For this time, all the rest depart away: +You Capulet; shall go along with me: +And, Montague, come you this afternoon, +To know our further pleasure in this case, +To old Free-town, our common judgment-place. +Once more, on pain of death, all men depart.]] + txt = txt..txt..txt..txt + txt = txt..txt..txt..txt + +for i=1,N do + res = md5(txt) +end +assert(res == 'a831e91e0f70eddcb70dc61c6f82f6cd') + diff --git a/bench/meteor.lua b/bench/meteor.lua new file mode 100644 index 0000000000..80588ab532 --- /dev/null +++ b/bench/meteor.lua @@ -0,0 +1,220 @@ + +-- Generate a decision tree based solver for the meteor puzzle. +local function generatesolver(countinit) + local pairs, ipairs, format = pairs, ipairs, string.format + local byte, min, sort = string.byte, math.min, table.sort + + -- Cached position to distance lookup. + local dist = setmetatable({}, { __index = function(t, xy) + local x = xy%10; local y = (xy-x)/10 + if (x+y)%2 == 1 then y = y + 1; x = 10 - x end + local d = xy + 256*x*x + 1024*y*y; t[xy] = d; return d + end}) + + -- Lookup table to validate a cell and to find its successor. + local ok = {} + for i=0,150 do ok[i] = false end + for i=99,0,-1 do + local x = i%10 + if ((i-x)/10+x)%2 == 0 then + ok[i] = i + (ok[i+1] and 1 or (ok[i+2] and 2 or 3)) + end + end + + -- Temporary board state for the island checks. + local islands, slide = {}, {20,22,24,26,28,31,33,35,37,39} + local bbc, bb = 0, {} + for i=0,19 do bb[i] = false; bb[i+80] = false end + for i=20,79 do bb[i] = ok[i] end + + -- Recursive flood fill algorithm. + local function fill(bb, p) + bbc = bbc + 1 + local n = p+2; if bb[n] then bb[n] = false; fill(bb, n) end + n = p-2; if bb[n] then bb[n] = false; fill(bb, n) end + n = p-9; if bb[n] then bb[n] = false; fill(bb, n) end + n = p-11; if bb[n] then bb[n] = false; fill(bb, n) end + n = p+9; if bb[n] then bb[n] = false; fill(bb, n) end + n = p+11; if bb[n] then bb[n] = false; fill(bb, n) end + end + + -- Generate pruned, sliding decision trees. + local dtrees = {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}} + local rot = { nil, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} } + for k=0,9 do + -- Generate 10 initial pieces from line noise. :-) + local t = { 60, 62, byte("@BMBIK@KT@GPIKR@IKIKT@GK@KM@BG", k*3+1, k*3+3) } + rot[1] = t + for i,xy in ipairs(t) do + local x = xy%10; local y = (xy-x-60)/10 + -- Add 11 more variations by rotating and flipping. + for j=2,12 do + if j == 7 then y = -y else x,y = (x+3*y)/2, (y-x)/2 end + rot[j][i] = x+10*y + end + end + for r,v in ipairs(rot) do + -- Exploit symmetry and leave out half of the orientations of one piece. + -- The selected piece gives the best reduction of the solution space. + if k ~= 3 or r%2 == 0 then + -- Normalize to origin, add distance, sort by distance from origin. + local m = min(v[1], v[2], v[3], v[4], v[5]) + for i=1,5 do v[i] = dist[v[i]-m] end + sort(v) + local v2, v3, v4, v5 = v[2]%256, v[3]%256, v[4]%256, v[5]%256 + -- Slide the piece across 2 rows, prune the tree, check for islands. + for j,p in ipairs(slide) do + bb[p] = false + if ok[p+v2] and ok[p+v3] and ok[p+v4] and ok[p+v5] then -- Prune. + for i=p+1,79 do bb[i] = ok[i] end -- Clear remaining board. + bb[p+v2] = false; bb[p+v3] = false -- Add piece. + bb[p+v4] = false; bb[p+v5] = false + bbc = j -- Flood fill and count the filled positions. + if bb[71] then bb[71] = false; fill(bb, 71) end -- Lower left. + if bb[79] then bb[79] = false; fill(bb, 79) end -- Lower right. + local di = 0 + if bbc < 22 then bbc = 26 + elseif bbc < 26 then -- Island found, locate it, fill from above. + for i=p+2,79 do if bb[i] then di = i-p; break end end + for i=p-9,p-1 do if ok[i] then fill(bb, i) bbc = bbc - 1 end end + end + if bbc == 26 then -- Prune boards with static islands. + local tb = dtrees[j] -- Build decision tree in distance order. + local ta = tb[v2]; if not ta then ta = {}; tb[v2] = ta end + tb = ta[v3]; if not tb then tb = {}; ta[v3] = tb end + ta = tb[v4]; if not ta then ta = {}; tb[v4] = ta; islands[ta] = di + elseif islands[ta] ~= di then islands[ta] = 0 end + ta[v5] = di*10+k -- Leaves hold island check and piece number. + end + end + end + end + end + end + + local s = "local u0,u1,u2,u3,u4,u5,u6,u7,u8,u9" -- Piece use flags. + for p=0,99 do if ok[p] then s = s..",b"..p end end -- Board cells. + s = s.."\n"..[[ +local countinit = ... +local count = countinit +local bmin, bmax, pcs = 9, 0, {} +local smin, smax +local write, reverse = io.write, string.reverse + +-- Print min/max boards. +local function printboard(s) + local flip = true + for x in string.gmatch(string.gsub(s, ".", "%1 "), "..........") do + write(x, flip and "\n " or "\n") + flip = not flip + end + write("\n") +end + +-- Print result. +local function printresult() + write(countinit-count, " solutions found\n\n") + printboard(smin) + printboard(smax) +end + +-- Generate piece lookup array from the order of use. +local function genp() + local p = pcs + p[u0] = "0" p[u1] = "1" p[u2] = "2" p[u3] = "3" p[u4] = "4" + p[u5] = "5" p[u6] = "6" p[u7] = "7" p[u8] = "8" p[u9] = "9" + return p +end + +-- Goal function. +local function f91(k) + if k ~= 10 then return end + count = count - 2 -- Need to count the symmetric solution, too. + repeat + -- Quick precheck before constructing the string. + local b0, b99 = b0, b99 + if b0 <= bmin then bmin = b0 elseif b0 >= bmax then bmax = b0 + elseif b99 <= bmin then bmin = b99 elseif b99 >= bmax then bmax = b99 + else break end + -- Translate the filled board to a string. + local p = genp() + local s = p[b0] ]] + for p=2,99 do if ok[p] then s = s.."..p[b"..p.."]" end end + s = s..[[ + -- Remember min/max boards, dito for the symmetric board. + if not smin then smin = s; smax = s + elseif s < smin then smin = s elseif s > smax then smax = s end + s = reverse(s) + if s < smin then smin = s elseif s > smax then smax = s end + until true + if count <= 0 then error() end -- Early abort if max count given. +end +local f93 = f91 +]] + + -- Recursively convert the decision tree to Lua code. + local function codetree(tree, d, p, pn) + local found, s = false, "" + d = d + 1 + for a,t in pairs(tree) do + local b = p+a + if b < 100 then -- Prune the tree at the lower border. + local pp = b ~= pn and pn or ok[b] -- Find maximum successor function. + if d >= 5 then -- Try to place the last cell of a piece and advance. + found = true + local u = t%10 + local di = (t-u)/10 + if di ~= 0 and d == 5 then + di = di + p; if pp == di then pp = ok[di] end + s = format("%sif b%d and not u%d and not b%d then b%d=k u%d=k f%d(k) u%d=N b%d=N end\n", + s, di, u, b, b, u, pp, u, b) + else + s = format("%sif not u%d and not b%d then b%d=k u%d=k f%d(k) u%d=N b%d=N end\n", + s, u, b, b, u, pp, u, b) + end + else -- Try to place an intermediate cell. + local di = d ~= 4 and 0 or islands[t] + if di == 0 then + local st = codetree(t, d, p, pp) + if st then + found = true + s = format("%sif not b%d then b%d=k\n%sb%d=N end\n", s, b, b, st, b) + end + else -- Combine island checks. + di = di + p; if pp == di then pp = ok[di] end + local st = codetree(t, 6, p, pp) + if st then + found = true + s = format("%sif b%d and not b%d then b%d=k\n%sb%d=N end\n", s, di, b, b, st, b) + end + end + end + end + end + return found and s + end + + -- Embed the decision tree into a function hierarchy. + local j = 5 + for p=88,0,-1 do + local pn = ok[p] + if pn then + s = format("%slocal function f%d(k)\nlocal N if b%d then return f%d(k) end k=k+1 b%d=k\n%sb%d=N end\n", + s, p, p, pn, p, codetree(dtrees[j], 1, p, pn), p) + j = j - 1; if j == 0 then j = 10 end + end + end + + -- Compile and return solver function and result getter. + return loadstring(s.."return f0, printresult\n", "solver")(countinit) +end + +-- Generate the solver function hierarchy. +local solver, printresult = generatesolver(tonumber(arg and arg[1]) or 10000) + +-- The optimizer for LuaJIT 1.1.x is not helpful here, so turn it off. +if jit and jit.opt and jit.version_num < 10200 then jit.opt.start(0) end + +-- Run the solver protected to get partial results (max count or ctrl-c). +pcall(solver, 0) +printresult() diff --git a/bench/nbody.lua b/bench/nbody.lua new file mode 100644 index 0000000000..e0ff8f7712 --- /dev/null +++ b/bench/nbody.lua @@ -0,0 +1,119 @@ + +local sqrt = math.sqrt + +local PI = 3.141592653589793 +local SOLAR_MASS = 4 * PI * PI +local DAYS_PER_YEAR = 365.24 +local bodies = { + { -- Sun + x = 0, + y = 0, + z = 0, + vx = 0, + vy = 0, + vz = 0, + mass = SOLAR_MASS + }, + { -- Jupiter + x = 4.84143144246472090e+00, + y = -1.16032004402742839e+00, + z = -1.03622044471123109e-01, + vx = 1.66007664274403694e-03 * DAYS_PER_YEAR, + vy = 7.69901118419740425e-03 * DAYS_PER_YEAR, + vz = -6.90460016972063023e-05 * DAYS_PER_YEAR, + mass = 9.54791938424326609e-04 * SOLAR_MASS + }, + { -- Saturn + x = 8.34336671824457987e+00, + y = 4.12479856412430479e+00, + z = -4.03523417114321381e-01, + vx = -2.76742510726862411e-03 * DAYS_PER_YEAR, + vy = 4.99852801234917238e-03 * DAYS_PER_YEAR, + vz = 2.30417297573763929e-05 * DAYS_PER_YEAR, + mass = 2.85885980666130812e-04 * SOLAR_MASS + }, + { -- Uranus + x = 1.28943695621391310e+01, + y = -1.51111514016986312e+01, + z = -2.23307578892655734e-01, + vx = 2.96460137564761618e-03 * DAYS_PER_YEAR, + vy = 2.37847173959480950e-03 * DAYS_PER_YEAR, + vz = -2.96589568540237556e-05 * DAYS_PER_YEAR, + mass = 4.36624404335156298e-05 * SOLAR_MASS + }, + { -- Neptune + x = 1.53796971148509165e+01, + y = -2.59193146099879641e+01, + z = 1.79258772950371181e-01, + vx = 2.68067772490389322e-03 * DAYS_PER_YEAR, + vy = 1.62824170038242295e-03 * DAYS_PER_YEAR, + vz = -9.51592254519715870e-05 * DAYS_PER_YEAR, + mass = 5.15138902046611451e-05 * SOLAR_MASS + } +} + +local function advance(bodies, nbody, dt) + for i=1,nbody do + local bi = bodies[i] + local bix, biy, biz, bimass = bi.x, bi.y, bi.z, bi.mass + local bivx, bivy, bivz = bi.vx, bi.vy, bi.vz + for j=i+1,nbody do + local bj = bodies[j] + local dx, dy, dz = bix-bj.x, biy-bj.y, biz-bj.z + local mag = sqrt(dx*dx + dy*dy + dz*dz) + mag = dt / (mag * mag * mag) + local bm = bj.mass*mag + bivx = bivx - (dx * bm) + bivy = bivy - (dy * bm) + bivz = bivz - (dz * bm) + bm = bimass*mag + bj.vx = bj.vx + (dx * bm) + bj.vy = bj.vy + (dy * bm) + bj.vz = bj.vz + (dz * bm) + end + bi.vx = bivx + bi.vy = bivy + bi.vz = bivz + bi.x = bix + dt * bivx + bi.y = biy + dt * bivy + bi.z = biz + dt * bivz + end +end + +local function energy(bodies, nbody) + local e = 0 + for i=1,nbody do + local bi = bodies[i] + local vx, vy, vz, bim = bi.vx, bi.vy, bi.vz, bi.mass + e = e + (0.5 * bim * (vx*vx + vy*vy + vz*vz)) + for j=i+1,nbody do + local bj = bodies[j] + local dx, dy, dz = bi.x-bj.x, bi.y-bj.y, bi.z-bj.z + local distance = sqrt(dx*dx + dy*dy + dz*dz) + e = e - ((bim * bj.mass) / distance) + end + end + return e +end + +local function offsetMomentum(b, nbody) + local px, py, pz = 0, 0, 0 + for i=1,nbody do + local bi = b[i] + local bim = bi.mass + px = px + (bi.vx * bim) + py = py + (bi.vy * bim) + pz = pz + (bi.vz * bim) + end + b[1].vx = -px / SOLAR_MASS + b[1].vy = -py / SOLAR_MASS + b[1].vz = -pz / SOLAR_MASS +end + +local N = tonumber(arg and arg[1]) or 1000 +local nbody = #bodies + +offsetMomentum(bodies, nbody) +io.write( string.format("%0.9f",energy(bodies, nbody)), "\n") +for i=1,N do advance(bodies, nbody, 0.01) end +io.write( string.format("%0.9f",energy(bodies, nbody)), "\n") diff --git a/bench/nsieve-bit-fp.lua b/bench/nsieve-bit-fp.lua new file mode 100644 index 0000000000..3971ec1f1e --- /dev/null +++ b/bench/nsieve-bit-fp.lua @@ -0,0 +1,37 @@ + +local floor, ceil = math.floor, math.ceil + +local precision = 50 -- Maximum precision of lua_Number (minus safety margin). +local onebits = (2^precision)-1 + +local function nsieve(p, m) + local cm = ceil(m/precision) + do local onebits = onebits; for i=0,cm do p[i] = onebits end end + local count, idx, bit = 0, 2, 2 + for i=2,m do + local r = p[idx] / bit + if r - floor(r) >= 0.5 then -- Bit set? + local kidx, kbit = idx, bit + for k=i+i,m,i do + kidx = kidx + i + while kidx >= cm do kidx = kidx - cm; kbit = kbit + kbit end + local x = p[kidx] + local r = x / kbit + if r - floor(r) >= 0.5 then p[kidx] = x - kbit*0.5 end -- Clear bit. + end + count = count + 1 + end + idx = idx + 1 + if idx >= cm then idx = 0; bit = bit + bit end + end + return count +end + +local N = tonumber(arg and arg[1]) or 1 +if N < 2 then N = 2 end +local primes = {} + +for i=0,2 do + local m = (2^(N-i))*10000 + io.write(string.format("Primes up to %8d %8d\n", m, nsieve(primes, m))) +end diff --git a/bench/nsieve-bit.lua b/bench/nsieve-bit.lua new file mode 100644 index 0000000000..820a372647 --- /dev/null +++ b/bench/nsieve-bit.lua @@ -0,0 +1,27 @@ + +local bit = require("bit") +local band, bxor, rshift, rol = bit.band, bit.bxor, bit.rshift, bit.rol + +local function nsieve(p, m) + local count = 0 + for i=0,rshift(m, 5) do p[i] = -1 end + for i=2,m do + if band(rshift(p[rshift(i, 5)], i), 1) ~= 0 then + count = count + 1 + for j=i+i,m,i do + local jx = rshift(j, 5) + p[jx] = band(p[jx], rol(-2, j)) + end + end + end + return count +end + +local N = tonumber(arg and arg[1]) or 1 +if N < 2 then N = 2 end +local primes = {} + +for i=0,2 do + local m = (2^(N-i))*10000 + io.write(string.format("Primes up to %8d %8d\n", m, nsieve(primes, m))) +end diff --git a/bench/nsieve.lua b/bench/nsieve.lua new file mode 100644 index 0000000000..6de0524f95 --- /dev/null +++ b/bench/nsieve.lua @@ -0,0 +1,21 @@ + +local function nsieve(p, m) + for i=2,m do p[i] = true end + local count = 0 + for i=2,m do + if p[i] then + for k=i+i,m,i do p[k] = false end + count = count + 1 + end + end + return count +end + +local N = tonumber(arg and arg[1]) or 1 +if N < 2 then N = 2 end +local primes = {} + +for i=0,2 do + local m = (2^(N-i))*10000 + io.write(string.format("Primes up to %8d %8d\n", m, nsieve(primes, m))) +end diff --git a/bench/partialsums.lua b/bench/partialsums.lua new file mode 100644 index 0000000000..46bb9da35f --- /dev/null +++ b/bench/partialsums.lua @@ -0,0 +1,29 @@ + +local n = tonumber(arg[1]) +local function pr(fmt, x) io.write(string.format(fmt, x)) end + +local a1, a2, a3, a4, a5, a6, a7, a8, a9, alt = 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 +local sqrt, sin, cos = math.sqrt, math.sin, math.cos +for k=1,n do + local k2, sk, ck = k*k, sin(k), cos(k) + local k3 = k2*k + a1 = a1 + (2/3)^k + a2 = a2 + 1/sqrt(k) + a3 = a3 + 1/(k2+k) + a4 = a4 + 1/(k3*sk*sk) + a5 = a5 + 1/(k3*ck*ck) + a6 = a6 + 1/k + a7 = a7 + 1/k2 + a8 = a8 + alt/k + a9 = a9 + alt/(k+k-1) + alt = -alt +end +pr("%.9f\t(2/3)^k\n", a1) +pr("%.9f\tk^-0.5\n", a2) +pr("%.9f\t1/k(k+1)\n", a3) +pr("%.9f\tFlint Hills\n", a4) +pr("%.9f\tCookson Hills\n", a5) +pr("%.9f\tHarmonic\n", a6) +pr("%.9f\tRiemann Zeta\n", a7) +pr("%.9f\tAlternating Harmonic\n", a8) +pr("%.9f\tGregory\n", a9) diff --git a/bench/pidigits-nogmp.lua b/bench/pidigits-nogmp.lua new file mode 100644 index 0000000000..63a1cb0ee8 --- /dev/null +++ b/bench/pidigits-nogmp.lua @@ -0,0 +1,100 @@ + +-- Start of dynamically compiled chunk. +local chunk = [=[ + +-- Factory function for multi-precision number (mpn) operations. +local function fmm(fa, fb) + return loadstring([[ + return function(y, a, ka, b, kb) + local carry, n = 0, #a ]]..(fb == 0 and "" or [[ + local na, nb = n, #b -- Need to adjust lengths. 1 element suffices here. + if na > nb then b[na] = 0 elseif na < nb then a[nb] = 0; n = nb end + ]])..[[ + for i=1,n do -- Sum up all elements and propagate carry. + local x = a[i] ]]..(fa == 2 and "*ka" or "").. + (fb == 2 and "+b[i]*kb" or (fb == 1 and "+b[i]" or ""))..[[ + carry + if x < RADIX and x >= 0 then carry = 0; y[i] = x -- Check for overflow. + else local d = x % RADIX; carry = (x-d) / RADIX; y[i] = d end + end + y[n+1] = nil -- Truncate target. 1 element suffices here. + if carry == 0 then while n > 0 and y[n] == 0 do y[n] = nil end + elseif carry == -1 then y[n] = y[n] - RADIX else y[n+1] = carry end + ]]..(fb == 0 and "" or [[ -- Undo length adjustment. + if na > nb then b[na] = nil elseif na < nb and y ~= a then a[nb] = nil end + ]])..[[ + return y + end]])() +end + +-- Generate needed mpn functions. +local mm_kk, mm_k1, mm_k0, mm_11 = fmm(2, 2), fmm(2, 1), fmm(2, 0), fmm(1, 1) + +-- Choose the most efficient mpn function for y = a*ka + b*kb at run-time. +local function mm(y, a, ka, b, kb) + local f = mm_kk + if kb == 0 or #b == 0 then if ka == 1 then return a else f = mm_k0 end + elseif kb == 1 then if ka == 1 then f = mm_11 else f = mm_k1 end end + return f(y, a, ka, b, kb) +end + +-- Compose matrix with numbers on the right. +local function compose_r(aq,ar,as,at, bq,br,bs,bt) + mm(ar, ar,bq, at,br) mm(at, at,bt, ar,bs) + mm(as, as,bt, aq,bs) mm(aq, aq,bq, nil,0) +end + +-- Compose matrix with numbers on the left. +local function compose_l(aq,ar,as,at, bq,br,bs,bt) + mm(ar, ar,bt, aq,br) mm(at, at,bt, as,br) + mm(as, as,bq, at,bs) mm(aq, aq,bq, nil,0) +end + +-- Extract one digit. +local u, v, jj = {}, {}, 0 +local function extract(q,r,s,t, j) + local u = j == jj + 1 and mm(u, u,1, q,1) or mm(u, q,j, r,1); jj = j + local v = mm(v, t,1, s,j) + local nu, nv, y = #u, #v + if nu == nv then + if nu == 1 then y = u[1] / v[1] + else y = (u[nu]*RADIX + u[nu-1]) / (v[nv]*RADIX + v[nv-1]) end + elseif nu == nv+1 then y = (u[nu]*RADIX + u[nv]) / v[nv] + else return 0 end + return math.floor(y) +end + +-- Coroutine which yields successive digits of PI. +return coroutine.wrap(function() + local q, r, s, t, k = {1}, {}, {}, {1}, 1 + repeat + local y = extract(q,r,s,t, 3) + if y == extract(q,r,s,t, 4) then + coroutine.yield(y) + compose_r(q,r,s,t, 10, -10*y, 0, 1) + else + compose_l(q,r,s,t, k, 4*k+2, 0, 2*k+1) + k = k + 1 + end + until false +end) + +]=] -- End of dynamically compiled chunk. + +local N = tonumber(arg and arg[1]) or 27 +local RADIX = N < 6500 and 2^36 or 2^32 -- Avoid overflow. + +-- Substitute radix and compile chunk. +local pidigit = loadstring(string.gsub(chunk, "RADIX", tostring(RADIX)))() + +-- Print lines with 10 digits. +for i=10,N,10 do + for j=1,10 do io.write(pidigit()) end + io.write("\t:", i, "\n") +end + +-- Print remaining digits (if any). +local n10 = N % 10 +if n10 ~= 0 then + for i=1,n10 do io.write(pidigit()) end + io.write(string.rep(" ", 10-n10), "\t:", N, "\n") +end diff --git a/bench/ray.lua b/bench/ray.lua new file mode 100644 index 0000000000..2acc24c0bf --- /dev/null +++ b/bench/ray.lua @@ -0,0 +1,135 @@ +local sqrt = math.sqrt +local huge = math.huge + +local delta = 1 +while delta * delta + 1 ~= 1 do + delta = delta * 0.5 +end + +local function length(x, y, z) return sqrt(x*x + y*y + z*z) end +local function vlen(v) return length(v[1], v[2], v[3]) end +local function mul(c, x, y, z) return c*x, c*y, c*z end +local function unitise(x, y, z) return mul(1/length(x, y, z), x, y, z) end +local function dot(x1, y1, z1, x2, y2, z2) + return x1*x2 + y1*y2 + z1*z2 +end + +local function vsub(a, b) return a[1] - b[1], a[2] - b[2], a[3] - b[3] end +local function vdot(a, b) return dot(a[1], a[2], a[3], b[1], b[2], b[3]) end + + +local sphere = {} +function sphere:new(centre, radius) + self.__index = self + return setmetatable({centre=centre, radius=radius}, self) +end + +local function sphere_distance(self, origin, dir) + local vx, vy, vz = vsub(self.centre, origin) + local b = dot(vx, vy, vz, dir[1], dir[2], dir[3]) + local r = self.radius + local disc = r*r + b*b - vx*vx-vy*vy-vz*vz + if disc < 0 then return huge end + local d = sqrt(disc) + local t2 = b + d + if t2 < 0 then return huge end + local t1 = b - d + return t1 > 0 and t1 or t2 +end + +function sphere:intersect(origin, dir, best) + local lambda = sphere_distance(self, origin, dir) + if lambda < best[1] then + local c = self.centre + best[1] = lambda + local b2 = best[2] + b2[1], b2[2], b2[3] = + unitise( + origin[1] - c[1] + lambda * dir[1], + origin[2] - c[2] + lambda * dir[2], + origin[3] - c[3] + lambda * dir[3]) + end +end + +local group = {} +function group:new(bound) + self.__index = self + return setmetatable({bound=bound, children={}}, self) +end + +function group:add(s) + self.children[#self.children+1] = s +end + +function group:intersect(origin, dir, best) + local lambda = sphere_distance(self.bound, origin, dir) + if lambda < best[1] then + for _, c in ipairs(self.children) do + c:intersect(origin, dir, best) + end + end +end + +local hit = { 0, 0, 0 } +local ilight +local best = { huge, { 0, 0, 0 } } + +local function ray_trace(light, camera, dir, scene) + best[1] = huge + scene:intersect(camera, dir, best) + local b1 = best[1] + if b1 == huge then return 0 end + local b2 = best[2] + local g = vdot(b2, light) + if g >= 0 then return 0 end + hit[1] = camera[1] + b1*dir[1] + delta*b2[1] + hit[2] = camera[2] + b1*dir[2] + delta*b2[2] + hit[3] = camera[3] + b1*dir[3] + delta*b2[3] + best[1] = huge + scene:intersect(hit, ilight, best) + if best[1] == huge then + return -g + else + return 0 + end +end + +local function create(level, centre, radius) + local s = sphere:new(centre, radius) + if level == 1 then return s end + local gr = group:new(sphere:new(centre, 3*radius)) + gr:add(s) + local rn = 3*radius/sqrt(12) + for dz = -1,1,2 do + for dx = -1,1,2 do + gr:add(create(level-1, { centre[1] + rn*dx, centre[2] + rn, centre[3] + rn*dz }, radius*0.5)) + end + end + return gr +end + + +local level, n, ss = tonumber(arg[1]) or 9, tonumber(arg[2]) or 256, 4 +local iss = 1/ss +local gf = 255/(ss*ss) + +io.write(("P5\n%d %d\n255\n"):format(n, n)) +local light = { unitise(-1, -3, 2) } +ilight = { -light[1], -light[2], -light[3] } +local camera = { 0, 0, -4 } +local dir = { 0, 0, 0 } + +local scene = create(level, {0, -1, 0}, 1) + +for y = n/2-1, -n/2, -1 do + for x = -n/2, n/2-1 do + local g = 0 + for d = y, y+.99, iss do + for e = x, x+.99, iss do + dir[1], dir[2], dir[3] = unitise(e, d, n) + g = g + ray_trace(light, camera, dir, scene) + end + end + io.write(string.char(math.floor(0.5 + g*gf))) + end +end diff --git a/bench/recursive-ack.lua b/bench/recursive-ack.lua new file mode 100644 index 0000000000..fad30589bc --- /dev/null +++ b/bench/recursive-ack.lua @@ -0,0 +1,8 @@ +local function Ack(m, n) + if m == 0 then return n+1 end + if n == 0 then return Ack(m-1, 1) end + return Ack(m-1, (Ack(m, n-1))) -- The parentheses are deliberate. +end + +local N = tonumber(arg and arg[1]) or 10 +io.write("Ack(3,", N ,"): ", Ack(3,N), "\n") diff --git a/bench/recursive-fib.lua b/bench/recursive-fib.lua new file mode 100644 index 0000000000..ef9950decb --- /dev/null +++ b/bench/recursive-fib.lua @@ -0,0 +1,7 @@ +local function fib(n) + if n < 2 then return 1 end + return fib(n-2) + fib(n-1) +end + +local n = tonumber(arg[1]) or 10 +io.write(string.format("Fib(%d): %d\n", n, fib(n))) diff --git a/bench/revcomp.lua b/bench/revcomp.lua new file mode 100644 index 0000000000..34fe347bf9 --- /dev/null +++ b/bench/revcomp.lua @@ -0,0 +1,37 @@ + +local sub = string.sub +iubc = setmetatable({ + A="T", C="G", B="V", D="H", K="M", R="Y", + a="T", c="G", b="V", d="H", k="M", r="Y", + T="A", G="C", V="B", H="D", M="K", Y="R", U="A", + t="A", g="C", v="B", h="D", m="K", y="R", u="A", + N="N", S="S", W="W", n="N", s="S", w="W", +}, { __index = function(t, s) + local r = t[sub(s, 2)]..t[sub(s, 1, 1)]; t[s] = r; return r end }) + +local wcode = [=[ +return function(t, n) + if n == 1 then return end + local iubc, sub, write = iubc, string.sub, io.write + local s = table.concat(t, "", 1, n-1) + for i=#s-59,1,-60 do + write(]=] +for i=59,3,-4 do wcode = wcode.."iubc[sub(s, i+"..(i-3)..", i+"..i..")], " end +wcode = wcode..[=["\n") + end + local r = #s % 60 + if r ~= 0 then + for i=r,1,-4 do write(iubc[sub(s, i-3 < 1 and 1 or i-3, i)]) end + write("\n") + end +end +]=] +local writerev = loadstring(wcode)() + +local t, n = {}, 1 +for line in io.lines() do + local c = sub(line, 1, 1) + if c == ">" then writerev(t, n); io.write(line, "\n"); n = 1 + elseif c ~= ";" then t[n] = line; n = n + 1 end +end +writerev(t, n) diff --git a/bench/scimark-2010-12-20.lua b/bench/scimark-2010-12-20.lua new file mode 100644 index 0000000000..353acb7cbc --- /dev/null +++ b/bench/scimark-2010-12-20.lua @@ -0,0 +1,400 @@ +------------------------------------------------------------------------------ +-- Lua SciMark (2010-12-20). +-- +-- A literal translation of SciMark 2.0a, written in Java and C. +-- Credits go to the original authors Roldan Pozo and Bruce Miller. +-- See: http://math.nist.gov/scimark2/ +------------------------------------------------------------------------------ + +local SCIMARK_VERSION = "2010-12-10" +local SCIMARK_COPYRIGHT = "Copyright (C) 2006-2010 Mike Pall" + +local MIN_TIME = 2.0 +local RANDOM_SEED = 101009 -- Must be odd. +local SIZE_SELECT = "small" + +local benchmarks = { + "FFT", "SOR", "MC", "SPARSE", "LU", + small = { + FFT = { 1024 }, + SOR = { 100 }, + MC = { }, + SPARSE = { 1000, 5000 }, + LU = { 100 }, + }, + large = { + FFT = { 1048576 }, + SOR = { 1000 }, + MC = { }, + SPARSE = { 100000, 1000000 }, + LU = { 1000 }, + }, +} + +local abs, log, sin, floor = math.abs, math.log, math.sin, math.floor +local pi, clock = math.pi, os.clock +local format = string.format + +------------------------------------------------------------------------------ +-- Select array type: Lua tables or native (FFI) arrays +------------------------------------------------------------------------------ + +local darray, iarray + +local function array_init() + if jit and jit.status and jit.status() then + local ok, ffi = pcall(require, "ffi") + if ok then + darray = ffi.typeof("double[?]") + iarray = ffi.typeof("int[?]") + return + end + end + function darray(n) return {} end + iarray = darray +end + +------------------------------------------------------------------------------ +-- This is a Lagged Fibonacci Pseudo-random Number Generator with +-- j, k, M = 5, 17, 31. Pretty weak, but same as C/Java SciMark. +------------------------------------------------------------------------------ + +local rand, rand_init + +if jit and jit.status and jit.status() then + -- LJ2 has bit operations and zero-based arrays (internally). + local bit = require("bit") + local band, sar = bit.band, bit.arshift + function rand_init(seed) + local Rm, Rj, Ri = iarray(17), 16, 11 + for i=0,16 do Rm[i] = 0 end + for i=16,0,-1 do + seed = band(seed*9069, 0x7fffffff) + Rm[i] = seed + end + function rand() + local i = band(Ri+1, sar(Ri-16, 31)) + local j = band(Rj+1, sar(Rj-16, 31)) + Ri, Rj = i, j + local k = band(Rm[i] - Rm[j], 0x7fffffff) + Rm[j] = k + return k * (1.0/2147483647.0) + end + end +else + -- Better for standard Lua with one-based arrays and without bit operations. + function rand_init(seed) + local Rm, Rj = {}, 1 + for i=1,17 do Rm[i] = 0 end + for i=17,1,-1 do + seed = (seed*9069) % (2^31) + Rm[i] = seed + end + function rand() + local j, m = Rj, Rm + local h = j - 5 + if h < 1 then h = h + 17 end + local k = m[h] - m[j] + if k < 0 then k = k + 2147483647 end + m[j] = k + if j < 17 then Rj = j + 1 else Rj = 1 end + return k * (1.0/2147483647.0) + end + end +end + +local function random_vector(n) + local v = darray(n+1) + for x=1,n do v[x] = rand() end + return v +end + +local function random_matrix(m, n) + local a = {} + for y=1,m do + local v = darray(n+1) + a[y] = v + for x=1,n do v[x] = rand() end + end + return a +end + +------------------------------------------------------------------------------ +-- FFT: Fast Fourier Transform. +------------------------------------------------------------------------------ + +local function fft_bitreverse(v, n) + local j = 0 + for i=0,2*n-4,2 do + if i < j then + v[i+1], v[i+2], v[j+1], v[j+2] = v[j+1], v[j+2], v[i+1], v[i+2] + end + local k = n + while k <= j do j = j - k; k = k / 2 end + j = j + k + end +end + +local function fft_transform(v, n, dir) + if n <= 1 then return end + fft_bitreverse(v, n) + local dual = 1 + repeat + local dual2 = 2*dual + for i=1,2*n-1,2*dual2 do + local j = i+dual2 + local ir, ii = v[i], v[i+1] + local jr, ji = v[j], v[j+1] + v[j], v[j+1] = ir - jr, ii - ji + v[i], v[i+1] = ir + jr, ii + ji + end + local theta = dir * pi / dual + local s, s2 = sin(theta), 2.0 * sin(theta * 0.5)^2 + local wr, wi = 1.0, 0.0 + for a=3,dual2-1,2 do + wr, wi = wr - s*wi - s2*wr, wi + s*wr - s2*wi + for i=a,a+2*(n-dual2),2*dual2 do + local j = i+dual2 + local jr, ji = v[j], v[j+1] + local dr, di = wr*jr - wi*ji, wr*ji + wi*jr + local ir, ii = v[i], v[i+1] + v[j], v[j+1] = ir - dr, ii - di + v[i], v[i+1] = ir + dr, ii + di + end + end + dual = dual2 + until dual >= n +end + +function benchmarks.FFT(n) + local l2n = log(n)/log(2) + if l2n % 1 ~= 0 then + io.stderr:write("Error: FFT data length is not a power of 2\n") + os.exit(1) + end + local v = random_vector(n*2) + return function(cycles) + local norm = 1.0 / n + for p=1,cycles do + fft_transform(v, n, -1) + fft_transform(v, n, 1) + for i=1,n*2 do v[i] = v[i] * norm end + end + return ((5*n-2)*l2n + 2*(n+1)) * cycles + end +end + +------------------------------------------------------------------------------ +-- SOR: Jacobi Successive Over-Relaxation. +------------------------------------------------------------------------------ + +local function sor_run(mat, m, n, cycles, omega) + local om4, om1 = omega*0.25, 1.0-omega + m = m - 1 + n = n - 1 + for i=1,cycles do + for y=2,m do + local v, vp, vn = mat[y], mat[y-1], mat[y+1] + for x=2,n do + v[x] = om4*((vp[x]+vn[x])+(v[x-1]+v[x+1])) + om1*v[x] + end + end + end +end + +function benchmarks.SOR(n) + local mat = random_matrix(n, n) + return function(cycles) + sor_run(mat, n, n, cycles, 1.25) + return (n-1)*(n-1)*cycles*6 + end +end + +------------------------------------------------------------------------------ +-- MC: Monte Carlo Integration. +------------------------------------------------------------------------------ + +local function mc_integrate(cycles) + local under_curve = 0 + local rand = rand + for i=1,cycles do + local x = rand() + local y = rand() + if x*x + y*y <= 1.0 then under_curve = under_curve + 1 end + end + return (under_curve/cycles) * 4 +end + +function benchmarks.MC() + return function(cycles) + local res = mc_integrate(cycles) + assert(math.sqrt(cycles)*math.abs(res-math.pi) < 5.0, "bad MC result") + return cycles * 4 -- Way off, but same as SciMark in C/Java. + end +end + +------------------------------------------------------------------------------ +-- Sparse Matrix Multiplication. +------------------------------------------------------------------------------ + +local function sparse_mult(n, cycles, vy, val, row, col, vx) + for p=1,cycles do + for r=1,n do + local sum = 0 + for i=row[r],row[r+1]-1 do sum = sum + vx[col[i]] * val[i] end + vy[r] = sum + end + end +end + +function benchmarks.SPARSE(n, nz) + local nr = floor(nz/n) + local anz = nr*n + local vx = random_vector(n) + local val = random_vector(anz) + local vy, col, row = darray(n+1), iarray(nz+1), iarray(n+2) + row[1] = 1 + for r=1,n do + local step = floor(r/nr) + if step < 1 then step = 1 end + local rr = row[r] + row[r+1] = rr+nr + for i=0,nr-1 do col[rr+i] = 1+i*step end + end + return function(cycles) + sparse_mult(n, cycles, vy, val, row, col, vx) + return anz*cycles*2 + end +end + +------------------------------------------------------------------------------ +-- LU: Dense Matrix Factorization. +------------------------------------------------------------------------------ + +local function lu_factor(a, pivot, m, n) + local min_m_n = m < n and m or n + for j=1,min_m_n do + local jp, t = j, abs(a[j][j]) + for i=j+1,m do + local ab = abs(a[i][j]) + if ab > t then + jp = i + t = ab + end + end + pivot[j] = jp + if a[jp][j] == 0 then error("zero pivot") end + if jp ~= j then a[j], a[jp] = a[jp], a[j] end + if j < m then + local recp = 1.0 / a[j][j] + for k=j+1,m do + local v = a[k] + v[j] = v[j] * recp + end + end + if j < min_m_n then + for i=j+1,m do + local vi, vj = a[i], a[j] + local eij = vi[j] + for k=j+1,n do vi[k] = vi[k] - eij * vj[k] end + end + end + end +end + +local function matrix_alloc(m, n) + local a = {} + for y=1,m do a[y] = darray(n+1) end + return a +end + +local function matrix_copy(dst, src, m, n) + for y=1,m do + local vd, vs = dst[y], src[y] + for x=1,n do vd[x] = vs[x] end + end +end + +function benchmarks.LU(n) + local mat = random_matrix(n, n) + local tmp = matrix_alloc(n, n) + local pivot = iarray(n+1) + return function(cycles) + for i=1,cycles do + matrix_copy(tmp, mat, n, n) + lu_factor(tmp, pivot, n, n) + end + return 2.0/3.0*n*n*n*cycles + end +end + +------------------------------------------------------------------------------ +-- Main program. +------------------------------------------------------------------------------ + +local function printf(...) + io.write(format(...)) +end + +local function fmtparams(p1, p2) + if p2 then return format("[%d, %d]", p1, p2) + elseif p1 then return format("[%d]", p1) end + return "" +end + +local function measure(min_time, name, ...) + array_init() + rand_init(RANDOM_SEED) + local run = benchmarks[name](...) + local cycles = 1 + repeat + local tm = clock() + local flops = run(cycles, ...) + tm = clock() - tm + if tm >= min_time then + local res = flops / tm * 1.0e-6 + local p1, p2 = ... + printf("%-7s %8.2f %s\n", name, res, fmtparams(...)) + return res + end + cycles = cycles * 2 + until false +end + +printf("Lua SciMark %s based on SciMark 2.0a. %s.\n\n", + SCIMARK_VERSION, SCIMARK_COPYRIGHT) + +while arg and arg[1] do + local a = table.remove(arg, 1) + if a == "-noffi" then + package.preload.ffi = nil + elseif a == "-small" then + SIZE_SELECT = "small" + elseif a == "-large" then + SIZE_SELECT = "large" + elseif benchmarks[a] then + local p = benchmarks[SIZE_SELECT][a] + measure(MIN_TIME, a, tonumber(arg[1]) or p[1], tonumber(arg[2]) or p[2]) + return + else + printf("Usage: scimark [-noffi] [-small|-large] [BENCH params...]\n\n") + printf("BENCH -small -large\n") + printf("---------------------------------------\n") + for _,name in ipairs(benchmarks) do + printf("%-7s %-13s %s\n", name, + fmtparams(unpack(benchmarks.small[name])), + fmtparams(unpack(benchmarks.large[name]))) + end + printf("\n") + os.exit(1) + end +end + +local params = benchmarks[SIZE_SELECT] +local sum = 0 +for _,name in ipairs(benchmarks) do + sum = sum + measure(MIN_TIME, name, unpack(params[name])) +end +printf("\nSciMark %8.2f [%s problem sizes]\n", sum / #benchmarks, SIZE_SELECT) +io.flush() + diff --git a/bench/scimark-fft.lua b/bench/scimark-fft.lua new file mode 100644 index 0000000000..c05bb69a6b --- /dev/null +++ b/bench/scimark-fft.lua @@ -0,0 +1 @@ +require("scimark_lib").FFT(1024)(tonumber(arg and arg[1]) or 50000) diff --git a/bench/scimark-lu.lua b/bench/scimark-lu.lua new file mode 100644 index 0000000000..7636d994c6 --- /dev/null +++ b/bench/scimark-lu.lua @@ -0,0 +1 @@ +require("scimark_lib").LU(100)(tonumber(arg and arg[1]) or 5000) diff --git a/bench/scimark-sor.lua b/bench/scimark-sor.lua new file mode 100644 index 0000000000..e537e9867f --- /dev/null +++ b/bench/scimark-sor.lua @@ -0,0 +1 @@ +require("scimark_lib").SOR(100)(tonumber(arg and arg[1]) or 50000) diff --git a/bench/scimark-sparse.lua b/bench/scimark-sparse.lua new file mode 100644 index 0000000000..01a2258df9 --- /dev/null +++ b/bench/scimark-sparse.lua @@ -0,0 +1 @@ +require("scimark_lib").SPARSE(1000, 5000)(tonumber(arg and arg[1]) or 150000) diff --git a/bench/scimark_lib.lua b/bench/scimark_lib.lua new file mode 100644 index 0000000000..aeffd75a62 --- /dev/null +++ b/bench/scimark_lib.lua @@ -0,0 +1,297 @@ +------------------------------------------------------------------------------ +-- Lua SciMark (2010-03-15). +-- +-- A literal translation of SciMark 2.0a, written in Java and C. +-- Credits go to the original authors Roldan Pozo and Bruce Miller. +-- See: http://math.nist.gov/scimark2/ +------------------------------------------------------------------------------ + + +local SCIMARK_VERSION = "2010-03-15" + +local RANDOM_SEED = 101009 -- Must be odd. + +local abs, log, sin, floor = math.abs, math.log, math.sin, math.floor +local pi, clock = math.pi, os.clock + +local benchmarks = {} + +------------------------------------------------------------------------------ +-- This is a Lagged Fibonacci Pseudo-random Number Generator with +-- j, k, M = 5, 17, 31. Pretty weak, but same as C/Java SciMark. +------------------------------------------------------------------------------ + +local rand, rand_init + +if jit and jit.status and jit.status() then + -- LJ2 has bit operations and zero-based arrays (internally). + local bit = require("bit") + local band, sar = bit.band, bit.arshift + local Rm, Rj, Ri = {}, 0, 0 + for i=0,16 do Rm[i] = 0 end + function rand_init(seed) + Rj, Ri = 16, 11 + for i=16,0,-1 do + seed = band(seed*9069, 0x7fffffff) + Rm[i] = seed + end + end + function rand() + local i = band(Ri+1, sar(Ri-16, 31)) + local j = band(Rj+1, sar(Rj-16, 31)) + Ri, Rj = i, j + local k = band(Rm[i] - Rm[j], 0x7fffffff) + Rm[j] = k + return k * (1.0/2147483647.0) + end +else + -- Better for standard Lua with one-based arrays and without bit operations. + local Rm, Rj = {}, 1 + for i=1,17 do Rm[i] = 0 end + function rand_init(seed) + Rj = 1 + for i=17,1,-1 do + seed = (seed*9069) % (2^31) + Rm[i] = seed + end + end + function rand() + local j, m = Rj, Rm + local h = j - 5 + if h < 1 then h = h + 17 end + local k = m[h] - m[j] + if k < 0 then k = k + 2147483647 end + m[j] = k + if j < 17 then Rj = j + 1 else Rj = 1 end + return k * (1.0/2147483647.0) + end +end + +local function random_vector(n) + local v = {} + for x=1,n do v[x] = rand() end + return v +end + +local function random_matrix(m, n) + local a = {} + for y=1,m do + local v = {} + a[y] = v + for x=1,n do v[x] = rand() end + end + return a +end + +------------------------------------------------------------------------------ +-- FFT: Fast Fourier Transform. +------------------------------------------------------------------------------ + +local function fft_bitreverse(v, n) + local j = 0 + for i=0,2*n-4,2 do + if i < j then + v[i+1], v[i+2], v[j+1], v[j+2] = v[j+1], v[j+2], v[i+1], v[i+2] + end + local k = n + while k <= j do j = j - k; k = k / 2 end + j = j + k + end +end + +local function fft_transform(v, n, dir) + if n <= 1 then return end + fft_bitreverse(v, n) + local dual = 1 + repeat + local dual2 = 2*dual + for i=1,2*n-1,2*dual2 do + local j = i+dual2 + local ir, ii = v[i], v[i+1] + local jr, ji = v[j], v[j+1] + v[j], v[j+1] = ir - jr, ii - ji + v[i], v[i+1] = ir + jr, ii + ji + end + local theta = dir * pi / dual + local s, s2 = sin(theta), 2.0 * sin(theta * 0.5)^2 + local wr, wi = 1.0, 0.0 + for a=3,dual2-1,2 do + wr, wi = wr - s*wi - s2*wr, wi + s*wr - s2*wi + for i=a,a+2*(n-dual2),2*dual2 do + local j = i+dual2 + local jr, ji = v[j], v[j+1] + local dr, di = wr*jr - wi*ji, wr*ji + wi*jr + local ir, ii = v[i], v[i+1] + v[j], v[j+1] = ir - dr, ii - di + v[i], v[i+1] = ir + dr, ii + di + end + end + dual = dual2 + until dual >= n +end + +function benchmarks.FFT(n) + local l2n = log(n)/log(2) + if l2n % 1 ~= 0 then + io.stderr:write("Error: FFT data length is not a power of 2\n") + os.exit(1) + end + local v = random_vector(n*2) + return function(cycles) + local norm = 1.0 / n + for p=1,cycles do + fft_transform(v, n, -1) + fft_transform(v, n, 1) + for i=1,n*2 do v[i] = v[i] * norm end + end + return ((5*n-2)*l2n + 2*(n+1)) * cycles + end +end + +------------------------------------------------------------------------------ +-- SOR: Jacobi Successive Over-Relaxation. +------------------------------------------------------------------------------ + +local function sor_run(mat, m, n, cycles, omega) + local om4, om1 = omega*0.25, 1.0-omega + m = m - 1 + n = n - 1 + for i=1,cycles do + for y=2,m do + local v, vp, vn = mat[y], mat[y-1], mat[y+1] + for x=2,n do + v[x] = om4*((vp[x]+vn[x])+(v[x-1]+v[x+1])) + om1*v[x] + end + end + end +end + +function benchmarks.SOR(n) + local mat = random_matrix(n, n) + return function(cycles) + sor_run(mat, n, n, cycles, 1.25) + return (n-1)*(n-1)*cycles*6 + end +end + +------------------------------------------------------------------------------ +-- MC: Monte Carlo Integration. +------------------------------------------------------------------------------ + +local function mc_integrate(cycles) + local under_curve = 0 + local rand = rand + for i=1,cycles do + local x = rand() + local y = rand() + if x*x + y*y <= 1.0 then under_curve = under_curve + 1 end + end + return (under_curve/cycles) * 4 +end + +function benchmarks.MC() + return function(cycles) + local res = mc_integrate(cycles) + assert(math.sqrt(cycles)*math.abs(res-math.pi) < 5.0, "bad MC result") + return cycles * 4 -- Way off, but same as SciMark in C/Java. + end +end + +------------------------------------------------------------------------------ +-- Sparse Matrix Multiplication. +------------------------------------------------------------------------------ + +local function sparse_mult(n, cycles, vy, val, row, col, vx) + for p=1,cycles do + for r=1,n do + local sum = 0 + for i=row[r],row[r+1]-1 do sum = sum + vx[col[i]] * val[i] end + vy[r] = sum + end + end +end + +function benchmarks.SPARSE(n, nz) + local nr = floor(nz/n) + local anz = nr*n + local vx = random_vector(n) + local val = random_vector(anz) + local vy, col, row = {}, {}, {} + row[1] = 1 + for r=1,n do + local step = floor(r/nr) + if step < 1 then step = 1 end + local rr = row[r] + row[r+1] = rr+nr + for i=0,nr-1 do col[rr+i] = 1+i*step end + end + return function(cycles) + sparse_mult(n, cycles, vy, val, row, col, vx) + return anz*cycles*2 + end +end + +------------------------------------------------------------------------------ +-- LU: Dense Matrix Factorization. +------------------------------------------------------------------------------ + +local function lu_factor(a, pivot, m, n) + local min_m_n = m < n and m or n + for j=1,min_m_n do + local jp, t = j, abs(a[j][j]) + for i=j+1,m do + local ab = abs(a[i][j]) + if ab > t then + jp = i + t = ab + end + end + pivot[j] = jp + if a[jp][j] == 0 then error("zero pivot") end + if jp ~= j then a[j], a[jp] = a[jp], a[j] end + if j < m then + local recp = 1.0 / a[j][j] + for k=j+1,m do + local v = a[k] + v[j] = v[j] * recp + end + end + if j < min_m_n then + for i=j+1,m do + local vi, vj = a[i], a[j] + local eij = vi[j] + for k=j+1,n do vi[k] = vi[k] - eij * vj[k] end + end + end + end +end + +local function matrix_alloc(m, n) + local a = {} + for y=1,m do a[y] = {} end + return a +end + +local function matrix_copy(dst, src, m, n) + for y=1,m do + local vd, vs = dst[y], src[y] + for x=1,n do vd[x] = vs[x] end + end +end + +function benchmarks.LU(n) + local mat = random_matrix(n, n) + local tmp = matrix_alloc(n, n) + local pivot = {} + return function(cycles) + for i=1,cycles do + matrix_copy(tmp, mat, n, n) + lu_factor(tmp, pivot, n, n) + end + return 2.0/3.0*n*n*n*cycles + end +end + +rand_init(RANDOM_SEED) + +return benchmarks diff --git a/bench/series.lua b/bench/series.lua new file mode 100644 index 0000000000..f766cb3247 --- /dev/null +++ b/bench/series.lua @@ -0,0 +1,34 @@ + +local function integrate(x0, x1, nsteps, omegan, f) + local x, dx = x0, (x1-x0)/nsteps + local rvalue = ((x0+1)^x0 * f(omegan*x0)) / 2 + for i=3,nsteps do + x = x + dx + rvalue = rvalue + (x+1)^x * f(omegan*x) + end + return (rvalue + ((x1+1)^x1 * f(omegan*x1)) / 2) * dx +end + +local function series(n) + local sin, cos = math.sin, math.cos + local omega = math.pi + local t = {} + + t[1] = integrate(0, 2, 1000, 0, function() return 1 end) / 2 + t[2] = 0 + + for i=2,n do + t[2*i-1] = integrate(0, 2, 1000, omega*i, cos) + t[2*i] = integrate(0, 2, 1000, omega*i, sin) + end + + return t +end + +local n = tonumber(arg and arg[1]) or 10000 +local tm = os.clock() +local t = series(n) +tm = os.clock() - tm +assert(math.abs(t[1]-2.87295) < 0.00001) +io.write(string.format("size %d, %.2f s, %.1f iterations/s\n", + n, tm, (2*n-1)/tm)) diff --git a/bench/spectral-norm.lua b/bench/spectral-norm.lua new file mode 100644 index 0000000000..ecc8011208 --- /dev/null +++ b/bench/spectral-norm.lua @@ -0,0 +1,40 @@ + +local function A(i, j) + local ij = i+j-1 + return 1.0 / (ij * (ij-1) * 0.5 + i) +end + +local function Av(x, y, N) + for i=1,N do + local a = 0 + for j=1,N do a = a + x[j] * A(i, j) end + y[i] = a + end +end + +local function Atv(x, y, N) + for i=1,N do + local a = 0 + for j=1,N do a = a + x[j] * A(j, i) end + y[i] = a + end +end + +local function AtAv(x, y, t, N) + Av(x, t, N) + Atv(t, y, N) +end + +local N = tonumber(arg and arg[1]) or 100 +local u, v, t = {}, {}, {} +for i=1,N do u[i] = 1 end + +for i=1,10 do AtAv(u, v, t, N) AtAv(v, u, t, N) end + +local vBv, vv = 0, 0 +for i=1,N do + local ui, vi = u[i], v[i] + vBv = vBv + ui*vi + vv = vv + vi*vi +end +io.write(string.format("%0.9f\n", math.sqrt(vBv / vv))) diff --git a/bench/sum-file.lua b/bench/sum-file.lua new file mode 100644 index 0000000000..c9e618fdf5 --- /dev/null +++ b/bench/sum-file.lua @@ -0,0 +1,6 @@ + +local sum = 0 +for line in io.lines() do + sum = sum + line +end +io.write(sum, "\n") diff --git a/test/clib/cpptest.cpp b/test/clib/cpptest.cpp new file mode 100644 index 0000000000..a5893ed600 --- /dev/null +++ b/test/clib/cpptest.cpp @@ -0,0 +1,129 @@ + +#include + +extern "C" { +#define LUA_LIB +#include "lua.h" +#include "lauxlib.h" +#include "luajit.h" +} + +static int testobj_alloc; + +class TestObj { +public: + TestObj(int x) { foo = x; testobj_alloc = 1; } + ~TestObj() { testobj_alloc = 0; } +private: + int foo; +}; + +static int ct_alloc(lua_State *L) +{ + TestObj foo(1); + lua_pushlightuserdata(L, (void *)&foo); + lua_call(L, lua_gettop(L)-1, LUA_MULTRET); + if (lua_iscfunction(L, -1)) { + lua_CFunction f = lua_tocfunction(L, -1); + lua_pop(L, 1); + f(L); + } + return lua_gettop(L); +} + +static int ct_isalloc(lua_State *L) +{ + lua_pushboolean(L, testobj_alloc); + return 1; +} + +static int ct_usereg(lua_State *L) +{ + int n = luaL_checkint(L, 1); + int m = luaL_checkint(L, 2); + int i; + int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0; + for (i = 0; i < n; i++) { + a = (a + 1) ^ 0x12345678; + b = (b + 2) ^ 0x12345678; + c = (c + 3) ^ 0x12345678; + d = (d + 4) ^ 0x12345678; + e = (e + 5) ^ 0x12345678; + f = (f + 5) ^ 0x12345678; + if (i == m) { + if (i & 1) + lua_pcall(L, 1, 0, 0); + else + lua_call(L, 1, 0); + } + } + lua_pushinteger(L, a); + lua_pushinteger(L, b); + lua_pushinteger(L, c); + lua_pushinteger(L, d); + lua_pushinteger(L, e); + lua_pushinteger(L, f); + return 6; +} + +static int ct_catch(lua_State *L) +{ + try { + lua_call(L, lua_gettop(L)-1, LUA_MULTRET); + return lua_gettop(L); + } catch (const char *s) { + lua_pushstring(L, s); + } catch (...) { + lua_pushliteral(L, "catch ..."); + } + return 1; +} + +static int ct_throw(lua_State *L) +{ + const char *s = lua_tostring(L, 1); + throw(s); + return 0; +} + +static int ct_wrap(lua_State *L, lua_CFunction f) +{ + try { + return f(L); + } catch (const char *s) { + lua_pushstring(L, s); + } + return lua_error(L); +} + +static int ct_wrapon(lua_State *L) +{ + lua_pushlightuserdata(L, (void *)ct_wrap); + luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC|LUAJIT_MODE_ON); + return 0; +} + +static int ct_wrapoff(lua_State *L) +{ + luaJIT_setmode(L, 0, LUAJIT_MODE_WRAPCFUNC|LUAJIT_MODE_OFF); + return 0; +} + +static luaL_Reg ct_funcs[] = { + {"isalloc", ct_isalloc }, + {"alloc", ct_alloc }, + {"usereg", ct_usereg }, + {"catch", ct_catch }, + {"throw", ct_throw }, + {"wrapon", ct_wrapon }, + {"wrapoff", ct_wrapoff }, + {NULL, NULL} +}; + +extern "C" { +LUA_API int luaopen_cpptest(lua_State *L) +{ + luaL_register(L, "cpptest", ct_funcs); + return 1; +} +} diff --git a/test/clib/ctest.c b/test/clib/ctest.c new file mode 100644 index 0000000000..d257567b98 --- /dev/null +++ b/test/clib/ctest.c @@ -0,0 +1,339 @@ + +#define LUA_LIB +#include "lua.h" +#include "lauxlib.h" + +/* ------------------------------------------------------------------------ */ + +#ifdef _MSC_VER +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; +#else +#include +#define complex _Complex +#endif + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) +#ifdef _MSC_VER +#define LJ_FASTCALL __fastcall +#define LJ_STDCALL __stdcall +#else +#define LJ_FASTCALL __attribute__((fastcall)) +#define LJ_STDCALL __attribute__((stdcall)) +#endif +#endif + +typedef struct s_ii { int x, y; } s_ii; +typedef struct s_jj { int64_t x, y; } s_jj; +typedef struct s_ff { float x, y; } s_ff; +typedef struct s_dd { double x, y; } s_dd; +typedef struct s_8i { int a,b,c,d,e,f,g,h; } s_8i; + +LUA_API int call_i(int a) { return a+1; } +LUA_API int call_ii(int a, int b) { return a+b; } +LUA_API int call_10i(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { return a+b+c+d+e+f+g+h+i+j; } + +LUA_API int64_t call_10j(int a, int b, int c, int d, int e, int f, int g, int h, int i, int64_t j) { return a+b+c+d+e+f+g+h+i+j; } + +LUA_API int64_t call_ji(int64_t a, int b) { return a+b; } +LUA_API int64_t call_ij(int a, int64_t b) { return a+b; } +LUA_API int64_t call_jj(int64_t a, int64_t b) { return a+b; } + +LUA_API double call_dd(double a, double b) { return a+b; } +LUA_API double call_10d(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j) { return a+b+c+d+e+f+g+h+i+j; } + +LUA_API float call_ff(float a, float b) { return a+b; } +LUA_API float call_10f(float a, float b, float c, float d, float e, float f, float g, float h, float i, float j) { return a+b+c+d+e+f+g+h+i+j; } + +LUA_API double call_idifjd(int a, double b, int c, float d, int64_t e, double f) { return a+b+c+d+e+f; } + +LUA_API int call_p_i(int *a) { return *a+1; } +LUA_API int *call_p_p(int *a) { return a+1; } +LUA_API int call_pp_i(int *a, int *b) { return (int)(a-b); } + +#include + +LUA_API double call_ividi(int a, ...) +{ + double y; + va_list argp; + va_start(argp, a); + y = a; + y += va_arg(argp, int); + y += va_arg(argp, double); + y += va_arg(argp, int); + va_end(argp); + return y; +} + +#ifdef complex +LUA_API complex call_dd_cd(double a, double b) { return a+b*2i; } +LUA_API complex call_cd(complex a) { return a+1-2i; } +LUA_API complex call_cdcd(complex a, complex b) { return a+b; } + +LUA_API complex float call_ff_cf(float a, float b) { return a+b*2i; } +LUA_API complex float call_cf(complex float a) { return a+1-2i; } +LUA_API complex float call_cfcf(complex float a, complex float b) { return a+b; } +#endif + +LUA_API s_ii call_sii(s_ii a) { return a; } +LUA_API s_jj call_sjj(s_jj a) { return a; } +LUA_API s_ff call_sff(s_ff a) { return a; } +LUA_API s_dd call_sdd(s_dd a) { return a; } +LUA_API s_8i call_s8i(s_8i a) { return a; } +LUA_API s_ii call_siisii(s_ii a, s_ii b) +{ + s_ii c; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} +LUA_API s_ff call_sffsff(s_ff a, s_ff b) +{ + s_ff c; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} +LUA_API s_dd call_sddsdd(s_dd a, s_dd b) +{ + s_dd c; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} +LUA_API s_8i call_s8is8i(s_8i a, s_8i b) +{ + s_8i c; + c.a = a.a + b.a; + c.b = a.b + b.b; + c.c = a.c + b.c; + c.d = a.d + b.d; + c.e = a.e + b.e; + c.f = a.f + b.f; + c.g = a.g + b.g; + c.h = a.h + b.h; + return c; +} +LUA_API s_8i call_is8ii(int a, s_8i b, int c) +{ + b.a += a; + b.c += c; + return b; +} + +#ifdef LJ_FASTCALL +LUA_API int LJ_FASTCALL fastcall_void(void) { return 1; } +LUA_API int LJ_FASTCALL fastcall_i(int a) { return a+1; } +LUA_API int LJ_FASTCALL fastcall_ii(int a, int b) { return a+b; } +LUA_API int LJ_FASTCALL fastcall_iii(int a, int b, int c) { return a+b+c; } +LUA_API int64_t LJ_FASTCALL fastcall_ji(int64_t a, int b) { return a+b; } +LUA_API double LJ_FASTCALL fastcall_dd(double a, double b) { return a+b; } +LUA_API int LJ_FASTCALL fastcall_pp_i(int *a, int *b) { return (int)(a-b); } +LUA_API s_ii LJ_FASTCALL fastcall_siisii(s_ii a, s_ii b) +{ + s_ii c; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} +LUA_API s_dd LJ_FASTCALL fastcall_sddsdd(s_dd a, s_dd b) +{ + s_dd c; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} +#endif + +#if defined(LJ_STDCALL) && defined(_WIN32) +LUA_API int LJ_STDCALL stdcall_i(int a) { return a+1; } +LUA_API int LJ_STDCALL stdcall_ii(int a, int b) { return a+b; } +LUA_API double LJ_STDCALL stdcall_dd(double a, double b) { return a+b; } +LUA_API float LJ_STDCALL stdcall_ff(float a, float b) { return a+b; } +#endif + +/* ------------------------------------------------------------------------ */ + +static int ct_call(lua_State *L) +{ + int nresults = luaL_checkint(L, 1); + luaL_checkstack(L, nresults, "too many results"); + lua_call(L, lua_gettop(L)-2, nresults); + return lua_gettop(L)-1; +} + +static int ct_callon(lua_State *L) +{ + lua_State *co = lua_tothread(L, 1); + int nargs = lua_gettop(L)-1; + int nresults; + lua_xmove(L, co, nargs); + lua_call(co, nargs-1, LUA_MULTRET); + nresults = lua_gettop(co); + lua_xmove(co, L, nresults); + return nresults; +} + +static int ct_pcall_err(lua_State *L) +{ + int nresults = luaL_checkint(L, 1); + luaL_checkstack(L, nresults, "too many results"); + if (lua_pcall(L, lua_gettop(L)-2, nresults, 0)) + lua_error(L); + return lua_gettop(L)-1; +} + +static int ct_pcall(lua_State *L) +{ + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + +static int ct_xpcall(lua_State *L) +{ + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + +#define CO_RUN 0 /* running */ +#define CO_SUS 1 /* suspended */ +#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ +#define CO_DEAD 3 + +static const char *const statnames[] = + {"running", "suspended", "normal", "dead"}; + +static int costatus(lua_State *L, lua_State *co) { + if (L == co) return CO_RUN; + switch (lua_status(co)) { + case LUA_YIELD: + return CO_SUS; + case 0: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ + return CO_NOR; /* it is running */ + else if (lua_gettop(co) == 0) + return CO_DEAD; + else + return CO_SUS; /* initial state */ + } + default: /* some error occured */ + return CO_DEAD; + } +} + +static int auxresume(lua_State *L, lua_State *co, int narg) { + int status = costatus(L, co); + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + if (status != CO_SUS) { + lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + status = lua_resume(co, narg); + if (status == 0 || status == LUA_YIELD) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres + 1)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + +static int ct_resume(lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + +static int ct_auxwrap(lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + +static int ct_cocreate(lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int ct_wrap(lua_State *L) { + ct_cocreate(L); + lua_pushcclosure(L, ct_auxwrap, 1); + return 1; +} + +static int ct_yield(lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + +static int ct_lightud(lua_State *L) +{ + lua_pushlightuserdata(L, (void *)(ptrdiff_t)lua_tonumber(L, 1)); + return 1; +} + +static luaL_Reg ct_funcs[] = { + {"call", ct_call }, + {"callon", ct_callon }, + {"pcall", ct_pcall }, + {"xpcall", ct_xpcall }, + {"pcall_err", ct_pcall_err }, + {"resume", ct_resume }, + {"wrap", ct_wrap }, + {"yield", ct_yield }, + {"lightud", ct_lightud }, + {NULL, NULL} +}; + +LUA_API int luaopen_ctest(lua_State *L) +{ + luaL_register(L, "ctest", ct_funcs); + return 1; +} diff --git a/test/common/ffi_util.inc b/test/common/ffi_util.inc new file mode 100644 index 0000000000..1eee8dd933 --- /dev/null +++ b/test/common/ffi_util.inc @@ -0,0 +1,41 @@ +-- This should be turned into a proper module and not use globals. +-- Or combined into a generiv test utility module. With FFI +-- functionality turned off, if the FFI module is not built-in. + +local ffi = require("ffi") + +function checkfail(t, f) + f = f or ffi.typeof + for i=1,1e9 do + local tp = t[i] + if not tp then break end + assert(pcall(f, tp) == false, tp) + end +end + +function checktypes(t) + for i=1,1e9,3 do + local tp = t[i+2] + if not tp then break end + local id = ffi.typeof(tp) + assert(ffi.sizeof(id) == t[i], tp) + assert(ffi.alignof(id) == t[i+1], tp) + end +end + +function fails(f, ...) + if pcall(f, ...) ~= false then error("failure expected", 2) end +end + +local incroot = os.getenv("INCROOT") or "/usr/include" +local cdefs = os.getenv("CDEFS") or "" + +function include(name) + local flags = ffi.abi("32bit") and "-m32" or "-m64" + if string.sub(name, 1, 1) ~= "/" then name = incroot.."/"..name end + local fp = assert(io.popen("cc -E -P "..flags.." "..cdefs.." "..name)) + local s = fp:read("*a") + fp:close() + ffi.cdef(s) +end + diff --git a/test/ffi/ffi_arith_ptr.lua b/test/ffi/ffi_arith_ptr.lua new file mode 100644 index 0000000000..8cf890c608 --- /dev/null +++ b/test/ffi/ffi_arith_ptr.lua @@ -0,0 +1,106 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +ffi.cdef[[ +typedef struct { int a,b,c; } foo1_t; +void free(void *); +void *malloc(size_t); +struct incomplete; +]] + +do + local a = ffi.new("int[10]") + local p1 = a+0 + p1[0] = 1; + p1[1] = 2; + assert(a[0] == 1) + assert(a[1] == 2) + assert(a == p1) + assert(not (a ~= p1)) + assert(p1 <= a) + assert(a <= p1) + assert(not (p1 < a)) + assert(not (a < p1)) + assert(a ~= nil) + assert(not (a == nil)) + assert(p1 ~= nil) + assert(not (p1 == nil)) + + local p2 = a+2 + p2[0] = 3; + p2[1] = 4; + assert(a[2] == 3) + assert(a[3] == 4) + assert(p2 - p1 == 2) + assert(p1 - p2 == -2) + assert(p1 ~= p2) + assert(not (p1 == p2)) + assert(p1 < p2) + assert(p2 > p1) + assert(not (p1 > p2)) + assert(not (p2 < p1)) + assert(p1 <= p2) + assert(p2 >= p1) + assert(not (p1 >= p2)) + assert(not (p2 <= p1)) + + local p3 = a-2 + assert(p3[2] == 1) + assert(p3[3] == 2) + local p4 = a+(-3) + assert(p4[5] == 3) + assert(p4[6] == 4) + -- bad: adding two pointers or subtracting a pointer + fails(function(p1, p2) return p1 + p2 end, p1, p2) + fails(function(p1) return 1 - p1 end, p1) + -- bad: subtracting different pointer types + fails(function(p1) return p1 - ffi.new("char[1]") end, p1) + -- but different qualifiers are ok + local b = ffi.cast("const int *", a+5) + assert(b - a == 5) +end + +do + local p1 = ffi.cast("void *", 0) + local p2 = ffi.cast("int *", 1) + assert(p1 == p1) + assert(p2 == p2) + assert(p1 ~= p2) + assert(p1 == nil) + assert(p2 ~= nil) +end + +do + local f1 = ffi.C.free + local f2 = ffi.C.malloc + local p1 = ffi.cast("void *", f1) + assert(f1 == f1) + assert(f1 ~= nil) + assert(f1 ~= f2) + assert(p1 == f1) + assert(p1 ~= f2) + assert(f1 < f2 or f1 > f2) + fails(function(f1) return f1 + 1 end, f1) +end + +do + local s = ffi.new("foo1_t[10]") + local p1 = s+3 + p1.a = 1; p1.b = 2; p1.c = 3 + p1[1].a = 4; p1[1].b = 5; p1[1].c = 6 + assert(s[3].a == 1 and s[3].b == 2 and s[3].c == 3) + assert(s[4].a == 4 and s[4].b == 5 and s[4].c == 6) + local p2 = s+6 + assert(p2 - p1 == 3) + assert(p1 - p2 == -3) +end + +do + local mem = ffi.new("int[1]") + local p = ffi.cast("struct incomplete *", mem) + fails(function(p) return p+1 end, p) + local ok, err = pcall(function(p) return p[1] end, p) + assert(not ok and err:match("size.*unknown")) +end + diff --git a/test/ffi/ffi_bit64.lua b/test/ffi/ffi_bit64.lua new file mode 100644 index 0000000000..46cf60143c --- /dev/null +++ b/test/ffi/ffi_bit64.lua @@ -0,0 +1,120 @@ +local ffi = require("ffi") +local bit = require("bit") + +local tobit, bnot, bswap = bit.tobit, bit.bnot, bit.bswap +local band, bor, bxor = bit.band, bit.bor, bit.bxor +local shl, shr, sar = bit.lshift, bit.rshift, bit.arshift +local rol, ror = bit.rol, bit.ror + +ffi.cdef[[ +typedef enum { ZZI = -1 } ienum_t; +typedef enum { ZZU } uenum_t; +]] + +assert(tobit(0xfedcba9876543210ll) == 0x76543210) +assert(tobit(0xfedcba9876543210ull) == 0x76543210) + +assert(tostring(band(1ll, 1, 1ll, -1)) == "1LL") +assert(tostring(band(1ll, 1, 1ull, -1)) == "1ULL") + +assert(shl(10ll, 2) == 40) +assert(shl(10, 2ll) == 40) +assert(shl(10ll, 2ll) == 40) + +assert(bit.tohex(0x123456789abcdef0LL) == "123456789abcdef0") + +for _,tp in ipairs{ "int", "ienum_t", "uenum_t", "int64_t", "uint64_t"} do + local x = ffi.new(tp, 10) + local y = tobit(x) + local z = band(x) + assert(type(y) == "number" and y == 10) + assert(type(z) == "cdata" and z == 10) +end + +do + local x = ffi.new("uenum_t", -10) + local y = tobit(x) + local z = band(x) + assert(type(y) == "number") + assert(y == -10) + assert(type(z) == "cdata") + assert(z == 2^32-10) +end + +do + local a = 0x123456789abcdef0LL + local y1, y2, y3, y4, y5, y6 + for i=1,100 do + y1 = band(a, 0x000000005a5a5a5aLL) + y2 = band(a, 0x5a5a5a5a00000000LL) + y3 = band(a, 0xffffffff5a5a5a5aLL) + y4 = band(a, 0x5a5a5a5affffffffLL) + y5 = band(a, 0xffffffff00000000LL) + y6 = band(a, 0x00000000ffffffffLL) + end + assert(y1 == 0x000000001a185a50LL) + assert(y2 == 0x1210525800000000LL) + assert(y3 == 0x123456781a185a50LL) + assert(y4 == 0x121052589abcdef0LL) + assert(y5 == 0x1234567800000000LL) + assert(y6 == 0x000000009abcdef0LL) + for i=1,100 do + y1 = bor(a, 0x000000005a5a5a5aLL) + y2 = bor(a, 0x5a5a5a5a00000000LL) + y3 = bor(a, 0xffffffff5a5a5a5aLL) + y4 = bor(a, 0x5a5a5a5affffffffLL) + y5 = bor(a, 0xffffffff00000000LL) + y6 = bor(a, 0x00000000ffffffffLL) + end + assert(y1 == 0x12345678dafedefaLL) + assert(y2 == 0x5a7e5e7a9abcdef0LL) + assert(y3 == 0xffffffffdafedefaLL) + assert(y4 == 0x5a7e5e7affffffffLL) + assert(y5 == 0xffffffff9abcdef0LL) + assert(y6 == 0x12345678ffffffffLL) + for i=1,100 do + y1 = bxor(a, 0x000000005a5a5a5aLL) + y2 = bxor(a, 0x5a5a5a5a00000000LL) + y3 = bxor(a, 0xffffffff5a5a5a5aLL) + y4 = bxor(a, 0x5a5a5a5affffffffLL) + y5 = bxor(a, 0xffffffff00000000LL) + y6 = bxor(a, 0x00000000ffffffffLL) + end + assert(y1 == 0x12345678c0e684aaLL) + assert(y2 == 0x486e0c229abcdef0LL) + assert(y3 == 0xedcba987c0e684aaLL) + assert(y4 == 0x486e0c226543210fLL) + assert(y5 == 0xedcba9879abcdef0LL) + assert(y6 == 0x123456786543210fLL) +end + +do + local a, b = 0x123456789abcdef0LL, 0x31415926535898LL + for i=1,200 do + a = bxor(a, b); b = sar(b, 14) + shl(b, 50) + a = a - b; b = shl(b, 5) + sar(b, 59) + b = bxor(a, b); b = b - shl(b, 13) - shr(b, 51) + end + assert(b == -7993764627526027113LL) +end + +do + local a, b = 0x123456789abcdef0LL, 0x31415926535898LL + for i=1,200 do + a = bxor(a, b); b = rol(b, 14) + a = a - b; b = rol(b, 5) + b = bxor(a, b); b = b - rol(b, 13) + end + assert(b == -6199148037344061526LL) +end + +do + local a, b = 0x123456789abcdef0LL, 0x31415926535898LL + for i=1,200 do + a = bxor(a, b); b = rol(b, a) + a = a - b; b = shr(b, a) + shl(b, bnot(a)) + b = bxor(a, b); b = b - bswap(b) + end + assert(b == -8881785180777266821LL) +end + diff --git a/test/ffi/ffi_bitfield.lua b/test/ffi/ffi_bitfield.lua new file mode 100644 index 0000000000..cd0b1815bd --- /dev/null +++ b/test/ffi/ffi_bitfield.lua @@ -0,0 +1,108 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +do + local x = ffi.new([[ + union { + uint32_t u; + struct { int a:10,b:10,c:11,d:1; }; + struct { unsigned int e:10,f:10,g:11,h:1; }; + struct { int8_t i:4,j:5,k:5,l:3; }; + struct { _Bool b0:1,b1:1,b2:1,b3:1; }; + } + ]]) + + -- bitfield access + x.u = 0xffffffff + assert(x.a == -1 and x.b == -1 and x.c == -1 and x.d == -1) + assert(x.e == 1023 and x.f == 1023 and x.g == 2047 and x.h == 1) + assert(x.i == -1 and x.j == -1 and x.k == -1 and x.l == -1) + assert(x.b0 == true and x.b1 == true and x.b2 == true and x.b3 == true) + x.u = 0x12345678 + if ffi.abi("le") then + assert(x.a == -392 and x.b == 277 and x.c == 291 and x.d == 0) + assert(x.e == 632 and x.f == 277 and x.g == 291 and x.h == 0) + assert(x.i == -8 and x.j == -10 and x.k == -12 and x.l == 1) + assert(x.b0 == false and x.b1 == false and x.b2 == false and x.b3 == true) + else + assert(x.a == 72 and x.b == -187 and x.c == 828 and x.d == 0) + assert(x.e == 72 and x.f == 837 and x.g == 828 and x.h == 0) + assert(x.i == 1 and x.j == 6 and x.k == 10 and x.l == -2) + assert(x.b0 == false and x.b1 == false and x.b2 == false and x.b3 == true) + end + x.u = 0xe8d30edc + if ffi.abi("le") then + assert(x.a == -292 and x.b == 195 and x.c == -371 and x.d == -1) + assert(x.e == 732 and x.f == 195 and x.g == 1677 and x.h == 1) + assert(x.i == -4 and x.j == 14 and x.k == -13 and x.l == -2) + assert(x.b0 == false and x.b1 == false and x.b2 == true and x.b3 == true) + else + assert(x.a == -93 and x.b == 304 and x.c == -146 and x.d == 0) + assert(x.e == 931 and x.f == 304 and x.g == 1902 and x.h == 0) + assert(x.i == -2 and x.j == -6 and x.k == 1 and x.l == -2) + assert(x.b0 == true and x.b1 == true and x.b2 == true and x.b3 == false) + end + + -- bitfield insert + x.u = 0xffffffff + x.a = 0 + if ffi.abi("le") then + assert(x.u == 0xfffffc00) + else + assert(x.u == 0x003fffff) + end + x.u = 0 + x.a = -1 + if ffi.abi("le") then + assert(x.u == 0x3ff) + else + assert(x.u == 0xffc00000) + end + x.u = 0xffffffff + x.b = 0 + if ffi.abi("le") then + assert(x.u == 0xfff003ff) + else + assert(x.u == 0xffc00fff) + end + x.u = 0 + x.b = -1 + if ffi.abi("le") then + assert(x.u == 0x000ffc00) + else + assert(x.u == 0x003ff000) + end + + -- cumulative bitfield insert + x.u = 0xffffffff + if ffi.abi("le") then + x.a = -392; x.b = 277; x.c = 291; x.d = 0 + else + x.a = 72; x.b = -187; x.c = 828; x.d = 0 + end + assert(x.u == 0x12345678) + x.u = 0 + if ffi.abi("le") then + x.a = -392; x.b = 277; x.c = 291; x.d = 0 + else + x.a = 72; x.b = -187; x.c = 828; x.d = 0 + end + assert(x.u == 0x12345678) + x.u = 0xffffffff + x.b0 = true; x.b1 = false; x.b2 = true; x.b3 = false + if ffi.abi("le") then + assert(x.u == 0xfffffff5) + else + assert(x.u == 0xafffffff) + end + x.u = 0 + x.b0 = true; x.b1 = false; x.b2 = true; x.b3 = false + if ffi.abi("le") then + assert(x.u == 0x00000005) + else + assert(x.u == 0xa0000000) + end + +end + diff --git a/test/ffi/ffi_call.lua b/test/ffi/ffi_call.lua new file mode 100644 index 0000000000..1eb5e906b1 --- /dev/null +++ b/test/ffi/ffi_call.lua @@ -0,0 +1,266 @@ + +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +local tonumber = tonumber + +ffi.cdef[[ +typedef struct s_ii { int x, y; } s_ii; +typedef struct s_jj { int64_t x, y; } s_jj; +typedef struct s_ff { float x, y; } s_ff; +typedef struct s_dd { double x, y; } s_dd; +typedef struct s_8i { int a,b,c,d,e,f,g,h; } s_8i; + +int call_i(int a); +int call_ii(int a, int b); +int call_10i(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j); + +typedef enum { XYZ } e_u; + +e_u call_ie(e_u a) asm("call_i"); + +int64_t call_ji(int64_t a, int b); +int64_t call_ij(int a, int64_t b); +int64_t call_jj(int64_t a, int64_t b); + +double call_dd(double a, double b); +double call_10d(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j); + +float call_ff(float a, float b); +float call_10f(float a, float b, float c, float d, float e, float f, float g, float h, float i, float j); + +double call_idifjd(int a, double b, int c, float d, int64_t e, double f); + +int call_p_i(int *a); +int *call_p_p(int *a); +int call_pp_i(int *a, int *b); + +double call_ividi(int a, ...); + +complex call_dd_cd(double a, double b); +complex call_cd(complex a); +complex call_cdcd(complex a, complex b); + +complex float call_ff_cf(float a, float b); +complex float call_cf(complex float a); +complex float call_cfcf(complex float a, complex float b); + +s_ii call_sii(s_ii a); +s_jj call_sjj(s_jj a); +s_ff call_sff(s_ff a); +s_dd call_sdd(s_dd a); +s_8i call_s8i(s_8i a); +s_ii call_siisii(s_ii a, s_ii b); +s_ff call_sffsff(s_ff a, s_ff b); +s_dd call_sddsdd(s_dd a, s_dd b); +s_8i call_s8is8i(s_8i a, s_8i b); +s_8i call_is8ii(int a, s_8i b, int c); + +int __fastcall fastcall_void(void); +int __fastcall fastcall_i(int a); +int __fastcall fastcall_ii(int a, int b); +int __fastcall fastcall_iii(int a, int b, int c); +int64_t __fastcall fastcall_ji(int64_t a, int b); +double __fastcall fastcall_dd(double a, double b); +int __fastcall fastcall_pp_i(int *a, int *b); +s_ii __fastcall fastcall_siisii(s_ii a, s_ii b); +s_dd __fastcall fastcall_sddsdd(s_dd a, s_dd b); + +int __stdcall stdcall_i(int a); +int __stdcall stdcall_ii(int a, int b); +double __stdcall stdcall_dd(double a, double b); +float __stdcall stdcall_ff(float a, float b); +]] + +local C = ffi.load("../clib/ctest") + +assert(C.call_i(-42) == -41) +assert(C.call_ii(-42, 17) == -42+17) +assert(C.call_10i(-42, 17, 12345, 9987, -100, 11, 51, 0x12345678, 338, -78901234) == -42+17+12345+9987-100+11+51+0x12345678+338-78901234) + +assert(C.call_ie(123) == 124) + +assert(tonumber(C.call_ji(0x123456789LL, -17)) == tonumber(0x123456789LL-17)) +assert(tonumber(C.call_ij(-17, 0x123456789LL)) == tonumber(0x123456789LL-17)) +assert(tonumber(C.call_jj(-42, 17)) == -42+17) +assert(tonumber(C.call_jj(0x123456789abcdef0LL, -0x789abcde99887766LL)) == tonumber(0x123456789abcdef0LL-0x789abcde99887766LL)) + +assert(C.call_dd(12.5, -3.25) == 12.5-3.25) +assert(C.call_10d(-42.5, 17.125, 12345.5, 9987, -100.625, 11, 51, 0x12345678, 338, -78901234.75) == -42.5+17.125+12345.5+9987-100.625+11+51+0x12345678+338-78901234.75) + +assert(C.call_ff(12.5, -3.25) == 12.5-3.25) +assert(C.call_10f(-42.5, 17.125, 12345.5, 9987, -100.625, 11, 51, 0x123456, 338, -789012.75) == -42.5+17.125+12345.5+9987-100.625+11+51+0x123456+338-789012.75) + +assert(C.call_idifjd(-42, 17.125, 0x12345, -100.625, 12345678901234, -789012.75) == -42+17.125+0x12345-100.625+12345678901234-789012.75) + +do + local a = ffi.new("int[10]", -42) + assert(C.call_p_i(a) == -42+1) + assert(tonumber(ffi.cast("intptr_t", C.call_p_p(a+3))) == tonumber(ffi.cast("intptr_t", a+4))) + assert(C.call_pp_i(a+8, a+5) == 3) +end + +-- vararg +assert(C.call_ividi(-42, ffi.new("int", 17), 12.5, ffi.new("int", 131)) == -42+17+12.5+131) + +-- complex +if pcall(function() return C.call_dd_cd end) then + do + local c = C.call_dd_cd(12.5, -3.25) + assert(c.re == 12.5 and c.im == -3.25*2) + end + do + local c1 = ffi.new("complex", 12.5, -3.25) + local cz = C.call_cd(c1) + assert(cz.re == 12.5+1 and cz.im == -3.25-2) + end + do + local c1 = ffi.new("complex", 12.5, -3.25) + local c2 = ffi.new("complex", -17.125, 100.625) + local cz = C.call_cdcd(c1, c2) + assert(cz.re == 12.5-17.125 and cz.im == -3.25+100.625) + end + + do + local c = C.call_ff_cf(12.5, -3.25) + assert(c.re == 12.5 and c.im == -3.25*2) + end + do + local c1 = ffi.new("complex float", 12.5, -3.25) + local cz = C.call_cf(c1) + assert(cz.re == 12.5+1 and cz.im == -3.25-2) + end + do + local c1 = ffi.new("complex float", 12.5, -3.25) + local c2 = ffi.new("complex float", -17.125, 100.625) + local cz = C.call_cfcf(c1, c2) + assert(cz.re == 12.5-17.125 and cz.im == -3.25+100.625) + end +end + +-- structs +do + local s1 = ffi.new("s_ii", -42, 17) + local sz = C.call_sii(s1) + assert(s1.x == -42 and s1.y == 17) + assert(sz.x == -42 and sz.y == 17) +end + +do + local s1 = ffi.new("s_jj", 0x123456789abcdef0LL, -0x789abcde99887766LL) + local sz = C.call_sjj(s1) + assert(s1.x == 0x123456789abcdef0LL) + assert(s1.y == -0x789abcde99887766LL) + assert(sz.x == 0x123456789abcdef0LL) + assert(sz.y == -0x789abcde99887766LL) +end + +do + local s1 = ffi.new("s_ff", 12.5, -3.25) + local sz = C.call_sff(s1) + assert(s1.x == 12.5 and s1.y == -3.25) + assert(sz.x == 12.5 and sz.y == -3.25) +end + +do + local s1 = ffi.new("s_dd", 12.5, -3.25) + local sz = C.call_sdd(s1) + assert(s1.x == 12.5 and s1.y == -3.25) + assert(sz.x == 12.5 and sz.y == -3.25) +end + +do + local s1 = ffi.new("s_8i", -42, 17, 12345, 9987, -100, 11, 51, 0x12345678) + local sz = C.call_s8i(s1) + assert(s1.a+s1.b+s1.c+s1.d+s1.e+s1.f+s1.g+s1.h == -42+17+12345+9987-100+11+51+0x12345678) + assert(sz.a+sz.b+sz.c+sz.d+sz.e+sz.f+sz.g+sz.h == -42+17+12345+9987-100+11+51+0x12345678) +end + +do + local s1 = ffi.new("s_ii", -42, 17) + local s2 = ffi.new("s_ii", 0x12345, -98765) + local sz = C.call_siisii(s1, s2) + assert(s1.x == -42 and s1.y == 17) + assert(s2.x == 0x12345 and s2.y == -98765) + assert(sz.x == -42+0x12345 and sz.y == 17-98765) +end + +do + local s1 = ffi.new("s_ff", 12.5, -3.25) + local s2 = ffi.new("s_ff", -17.125, 100.625) + local sz = C.call_sffsff(s1, s2) + assert(s1.x == 12.5 and s1.y == -3.25) + assert(s2.x == -17.125 and s2.y == 100.625) + assert(sz.x == 12.5-17.125 and sz.y == -3.25+100.625) +end + +do + local s1 = ffi.new("s_dd", 12.5, -3.25) + local s2 = ffi.new("s_dd", -17.125, 100.625) + local sz = C.call_sddsdd(s1, s2) + assert(s1.x == 12.5 and s1.y == -3.25) + assert(s2.x == -17.125 and s2.y == 100.625) + assert(sz.x == 12.5-17.125 and sz.y == -3.25+100.625) +end + +do + local s1 = ffi.new("s_8i", -42, 17, 12345, 9987, -100, 11, 51, 0x12345678) + local s2 = ffi.new("s_8i", 99, 311, 98765, -51, 312, 97, 17, 0x44332211) + local sz = C.call_s8is8i(s1, s2) + assert(s1.a+s1.b+s1.c+s1.d+s1.e+s1.f+s1.g+s1.h == -42+17+12345+9987-100+11+51+0x12345678) + assert(s2.a+s2.b+s2.c+s2.d+s2.e+s2.f+s2.g+s2.h == 99+311+98765-51+312+97+17+0x44332211) + assert(sz.a+sz.b+sz.c+sz.d+sz.e+sz.f+sz.g+sz.h == -42+17+12345+9987-100+11+51+0x12345678 + 99+311+98765-51+312+97+17+0x44332211) + assert(sz.a == -42+99) + assert(sz.h == 0x12345678+0x44332211) +end + +do + local s1 = ffi.new("s_8i", -42, 17, 12345, 9987, -100, 11, 51, 0x12345678) + local sz = C.call_is8ii(19, s1, -51) + assert(s1.a+s1.b+s1.c+s1.d+s1.e+s1.f+s1.g+s1.h == -42+17+12345+9987-100+11+51+0x12345678) + assert(sz.a+sz.b+sz.c+sz.d+sz.e+sz.f+sz.g+sz.h == -42+17+12345+9987-100+11+51+0x12345678 + 19-51) + assert(sz.a == -42+19) + assert(sz.c == 12345-51) +end + +-- target-specific +if jit.arch == "x86" then + assert(C.fastcall_void() == 1) + assert(C.fastcall_i(-42) == -41) + assert(C.fastcall_ii(-42, 17) == -42+17) + assert(C.fastcall_iii(-42, 17, 139) == -42+17+139) + assert(tonumber(C.fastcall_ji(0x123456789LL, -17)) == tonumber(0x123456789LL-17)) + assert(C.fastcall_dd(12.5, -3.25) == 12.5-3.25) + + do + local a = ffi.new("int[10]", -42) + assert(C.fastcall_pp_i(a+8, a+5) == 3) + end + + do + local s1 = ffi.new("s_ii", -42, 17) + local s2 = ffi.new("s_ii", 0x12345, -98765) + local sz = C.fastcall_siisii(s1, s2) + assert(s1.x == -42 and s1.y == 17) + assert(s2.x == 0x12345 and s2.y == -98765) + assert(sz.x == -42+0x12345 and sz.y == 17-98765) + end + + do + local s1 = ffi.new("s_dd", 12.5, -3.25) + local s2 = ffi.new("s_dd", -17.125, 100.625) + local sz = C.fastcall_sddsdd(s1, s2) + assert(s1.x == 12.5 and s1.y == -3.25) + assert(s2.x == -17.125 and s2.y == 100.625) + assert(sz.x == 12.5-17.125 and sz.y == -3.25+100.625) + end + + if jit.os == "Windows" then + assert(C.stdcall_i(-42) == -41) + assert(C.stdcall_ii(-42, 17) == -42+17) + assert(C.stdcall_dd(12.5, -3.25) == 12.5-3.25) + assert(C.stdcall_ff(12.5, -3.25) == 12.5-3.25) + end +end + diff --git a/test/ffi/ffi_callback.lua b/test/ffi/ffi_callback.lua new file mode 100644 index 0000000000..1fd14bd0d2 --- /dev/null +++ b/test/ffi/ffi_callback.lua @@ -0,0 +1,158 @@ + +local ffi = require("ffi") + +ffi.cdef[[ +void qsort(void *base, size_t nmemb, size_t size, + int (*compar)(const uint8_t *, const uint8_t *)); +]] + +do + local cb = ffi.cast("int (*)(int, int, int)", function(a, b, c) + return a+b+c + end) + + assert(cb(10, 99, 13) == 122) + + -- Don't compile call to blacklisted function. + for i=1,200 do + if i > 60 then assert(cb(10, 99, 13) == 122) end + end +end + +do + assert(ffi.cast("int64_t (*)(int64_t, int64_t, int64_t)", function(a, b, c) + return a+b+c + end)(12345678901234567LL, 70000000000000001LL, 10000000909090904LL) == + 12345678901234567LL+70000000000000001LL+10000000909090904LL) + + assert(ffi.cast("double (*)(double, float, double)", function(a, b, c) + return a+b+c + end)(7.125, -123.25, 9999.33) == 7.125-123.25+9999.33) + + assert(ffi.cast("double (*)(int, double)", function(a, b) + return a+b + end)(12345, 7.125) == 12345 + 7.125) + + assert(ffi.cast("float (*)(double, float, double)", function(a, b, c) + return a+b+c + end)(7.125, -123.25, 9999.33) == 9883.205078125) + + assert(ffi.cast("int (*)(int, int, int, int, int, int, int, int, int, int)", + function(a, b, c, d, e, f, g, h, i, j) + return a+b+c+d+e+f+g+h+i+j + end)(-42, 17, 12345, 9987, -100, 11, 51, 0x12345678, 338, -78901234) == + -42+17+12345+9987-100+11+51+0x12345678+338-78901234) + + assert(ffi.cast("double (*)(double, double, double, double, double, double, double, double, double, double)", + function(a, b, c, d, e, f, g, h, i, j) + return a+b+c+d+e+f+g+h+i+j + end)(-42.5, 17.125, 12345.5, 9987, -100.625, 11, 51, 0x12345678, 338, -78901234.75) == + -42.5+17.125+12345.5+9987-100.625+11+51+0x12345678+338-78901234.75) +end + +-- Target-specific tests. +if jit.arch == "x86" then + assert(ffi.cast("__fastcall int (*)(int, int, int)", function(a, b, c) + return a+b+c + end)(10, 99, 13) == 122) + + assert(ffi.cast("__stdcall int (*)(int, int, int)", function(a, b, c) + return a+b+c + end)(10, 99, 13) == 122) + + -- Test reordering. + assert(ffi.cast("int64_t __fastcall (*)(int64_t, int, int)", function(a, b, c) + return a+b+c + end)(12345678901234567LL, 12345, 989797123) == + 12345678901234567LL+12345+989797123) +end + +-- Error handling. +do + local function f() + return + end -- Error for result conversion triggered here. + local ok, err = pcall(ffi.cast("int (*)(void)", f)) + assert(ok == false) + assert(string.match(err, ":"..debug.getinfo(f, "S").lastlinedefined..":")) + + assert(pcall(ffi.cast("int (*)(void)", function() end)) == false) + assert(pcall(ffi.cast("int (*)(void)", function() error("test") end)) == false) + assert(pcall(ffi.cast("int (*)(void)", function(a) return a+1 end)) == false) + + assert(pcall(ffi.cast("int (*)(int,int,int,int, int,int,int,int, int)", function() error("test") end), 1,1,1,1, 1,1,1,1, 1) == false) + assert(pcall(ffi.cast("int (*)(int,int,int,int, int,int,int,int, int)", function() error("test") end), 1,1,1,1, 1,1,1,1, 1) == false) +end + +do + local function cmp(pa, pb) + local a, b = pa[0], pb[0] + if a < b then + return -1 + elseif a > b then + return 1 + else + return 0 + end + end + + local arr = ffi.new("uint8_t[?]", 256) + for i=0,255 do arr[i] = math.random(0, 255) end + ffi.C.qsort(arr, 256, 1, cmp) + for i=0,254 do assert(arr[i] <= arr[i+1]) end +end + +if ffi.abi"win" then + ffi.cdef[[ + typedef int (__stdcall *WNDENUMPROC)(void *hwnd, intptr_t l); + int EnumWindows(WNDENUMPROC func, intptr_t l); + int SendMessageA(void *hwnd, uint32_t msg, int w, intptr_t l); + enum { WM_GETTEXT = 13 }; + ]] + + local C = ffi.C + local buf = ffi.new("char[?]", 256) + local lbuf = ffi.cast("intptr_t", buf) + local count = 0 + C.EnumWindows(function(hwnd, l) + if C.SendMessageA(hwnd, C.WM_GETTEXT, 255, lbuf) ~= 0 then + count = count + 1 + end + return true + end, 0) + assert(count > 10) +end + +do + local cb = ffi.cast("int(*)(void)", function() return 1 end) + assert(cb() == 1) + cb:free() + assert(pcall(cb) == false) + assert(pcall(cb.free, cb) == false) + assert(pcall(cb.set, cb, function() end) == false) + cb = ffi.cast("int(*)(void)", function() return 2 end) + assert(cb() == 2) + cb:set(function() return 3 end) + assert(cb() == 3) +end + +do + local ft = ffi.typeof("void(*)(void)") + local function f() end + local t = {} + for i=1,4 do + for i=1,400 do t[i] = ft(f) end + for i=1,400 do t[i]:free() end + end +end + +do + assert(ffi.cast("int (*)()", function() return string.byte"A" end)() == 65) +end + +do + local f = ffi.cast("void (*)(void)", function() debug.traceback() end) + debug.sethook(function() debug.sethook(nil, "", 0); f() end, "", 1) + local x +end + diff --git a/test/ffi/ffi_const.lua b/test/ffi/ffi_const.lua new file mode 100644 index 0000000000..d42133ad94 --- /dev/null +++ b/test/ffi/ffi_const.lua @@ -0,0 +1,113 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +ffi.cdef[[ +typedef struct s_t { + int v, w; +} s_t; + +typedef const s_t cs_t; + +typedef enum en_t { EE } en_t; + +typedef struct pcs_t { + int v; + const int w; +} pcs_t; + +typedef struct foo_t { + static const int cc = 17; + enum { CC = -37 }; + int i; + const int ci; + int bi:8; + const int cbi:8; + en_t e; + const en_t ce; + int a[10]; + const int ca[10]; + const char cac[10]; + s_t s; + cs_t cs; + pcs_t pcs1, pcs2; + const struct { + int ni; + }; + complex cx; + const complex ccx; + complex *cp; + const complex *ccp; +} foo_t; +]] + +do + local foo_t = ffi.typeof("foo_t") + local x = foo_t() + + -- constval + assert(x.cc == 17) + fails(function(x) x.cc = 1 end, x) + assert(x.CC == -37) + fails(function(x) x.CC = 1 end, x) + + -- fields + x.i = 1 + fails(function(x) x.ci = 1 end, x) + x.e = 1 + fails(function(x) x.ce = 1 end, x) + + -- bitfields + x.bi = 1 + fails(function(x) x.cbi = 1 end, x) + + -- arrays + do + local a = ffi.new("int[10]") + a[0] = 1 + local ca = ffi.new("const int[10]") + fails(function(ca) ca[0] = 1 end, ca) + end + x.a[0] = 1 + fails(function(x) x.ca[0] = 1 end, x) + fails(function(x) x.a = x.ca end, x) -- incompatible type + fails(function(x) x.ca = x.a end, x) + fails(function(x) x.ca = {} end, x) + fails(function(x) x.cac = "abc" end, x) + + -- structs + do + local s = ffi.new("s_t") + s.v = 1 + local cs = ffi.new("cs_t") + fails(function(cs) cs.v = 1 end, cs) + end + x.s.v = 1 + fails(function(x) x.cs.v = 1 end, x) + x.s = x.cs + fails(function(x) x.cs = x.s end, x) + fails(function(x) x.cs = {} end, x) + + -- pseudo-const structs + x.pcs1.v = 1 + fails(function(x) x.pcs1.w = 1 end, x) + fails(function(x) x.pcs1 = x.pcs2 end, x) + fails(function(x) x.pcs1 = {} end, x) + + -- transparent structs + local y = x.ni + fails(function(x) x.ni = 1 end, x) + + -- complex subtype is implicitly const and doesn't inherit const attribute + x.cx = 1 + fails(function(x) x.ccx = 1 end, x) + do + local cxa = ffi.new("complex[1]") + local ccxa = ffi.new("const complex[1]") + x.cp = cxa + x.ccp = cxa + fails(function(x) x.cp = ccxa end, x) + x.ccp = ccxa + end +end + diff --git a/test/ffi/ffi_convert.lua b/test/ffi/ffi_convert.lua new file mode 100644 index 0000000000..bd3fb1f903 --- /dev/null +++ b/test/ffi/ffi_convert.lua @@ -0,0 +1,787 @@ +local ffi = require("ffi") + +local ctest = require("ctest") + +dofile("../common/ffi_util.inc") + +local tonumber = tonumber + +ffi.cdef[[ +typedef struct bar_t { + int v, w; +} bar_t; +// Same structure, but treated as different struct. +typedef struct barx_t { + int v, w; +} barx_t; + +typedef struct nest_t { + int a,b; + struct { int c,d; }; + struct { int e1,e2; } e; + int f[2]; +} nest_t; + +typedef union uni_t { + int8_t a; + int16_t b; + int32_t c; +} uni_t; + +typedef struct arrinc_t { + int a[]; +} arrinc_t; + +typedef enum uenum_t { + UE0, UE71 = 71, UE72 +} uenum_t; + +typedef enum ienum_t { + IE0, IEM12 = -12, IEM11 +} ienum_t; + +typedef struct foo_t { + bool b; + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + int64_t i64; + uint64_t u64; + float f; + double d; + complex cf; + complex cd; + uint8_t __attribute__((mode(__V16QI__))) v16qi; + int __attribute__((mode(__V4SI__))) v4si; + double __attribute__((mode(__V2DF__))) v2df; + int *pi; + int *__ptr32 p32i; + const int *pci; + volatile int *pvi; + int **ppi; + const int **ppci; + void **ppv; + char *(*ppf)(char *, const char *); + int ai[10]; + int ai_guard; + int ai2[10]; + char ac[10]; + char ac_guard; + bar_t s; + bar_t s2; + bar_t *ps; + const bar_t *pcs; + barx_t sx; + struct { int a,b,c; } si; + int si_guard; + nest_t sn; + uni_t ui; + uenum_t ue; + ienum_t ie; +} foo_t; + +char *strcpy(char *dest, const char *src); +typedef struct FILE FILE; +int fileno(FILE *stream); +int _fileno(FILE *stream); +]] + +do + local foo_t = ffi.typeof("foo_t") + local sz = ffi.sizeof(foo_t) + local x = foo_t() + local y = foo_t() + ffi.fill(x, sz, 0xff) + ffi.fill(y, sz, 0xee) + + -- unknown member + fails(function(x) local a = x.bad end, x) + fails(function(x) x.bad = 1 end, x) + -- too many initializers + fails(function(x) x.d = ffi.new("double", 1,2) end, x) + + -- conversions to bool + x.b = false + assert(x.b == false) + x.b = true + assert(x.b == true) + x.b = 0 + assert(x.b == false) + x.b = 10 + assert(x.b == true) + y.b = false + x.b = y.b + assert(x.b == false) + x.b = ffi.new("bool", true) + assert(x.b == true) + x.b = ffi.cast("bool", false) + assert(x.b == false) + x.b = ffi.new("int32_t", 17) + assert(x.b == true) + x.b = ffi.new("int32_t", 0) + assert(x.b == false) + + -- conversions from bool + x.i32 = true + assert(x.i32 == 1) + x.i32 = false + assert(x.i32 == 0) + x.i8 = ffi.new("bool", true) + assert(x.i8 == 1) + x.i8 = ffi.new("bool", false) + assert(x.i8 == 0) + x.d = true + assert(x.d == 1) + x.d = ffi.new("bool", false) + assert(x.d == 0) + -- assignment of bool to other types is not allowed + fails(function(x) x.cd = true end, x) + fails(function(x) x.v4si = true end, x) + fails(function(x) x.ai = true end, x) + fails(function(x) x.s = true end, x) + + -- int to int conversions + x.i8 = 99 + assert(x.i8 == 99) + x.i8 = -99 + assert(x.i8 == -99) + x.i8 = 128 + assert(x.i8 == -128) + x.i8 = 0xfffe + assert(x.i8 == -2) + y.i8 = 91 + x.i8 = y.i8 + assert(x.i8 == 91) + x.i8 = ffi.new("uint8_t", 0xb7) + assert(x.i8 == -73) + x.i8 = ffi.new("int16_t", 0x7fa0) + assert(x.i8 == -96) + x.i8 = ffi.new("int32_t", 0xff91) + assert(x.i8 == -111) + x.i8 = ffi.new("int64_t", 0xff81) + assert(x.i8 == -127) + + x.u8 = 99 + assert(x.u8 == 99) + x.u8 = -99 + assert(x.u8 == 256-99) + x.u8 = 128 + assert(x.u8 == 128) + x.u8 = 0xfffe + assert(x.u8 == 0xfe) + x.u8 = ffi.new("int8_t", -73) + assert(x.u8 == 0xb7) + x.u8 = ffi.new("int16_t", 0x7fa0) + assert(x.u8 == 0xa0) + x.u8 = ffi.new("int32_t", 0xff91) + assert(x.u8 == 0x91) + x.u8 = ffi.new("int64_t", 0xff81) + assert(x.u8 == 0x81) + + x.i16 = 99 + assert(x.i16 == 99) + x.i16 = -99 + assert(x.i16 == -99) + x.i16 = 32768 + assert(x.i16 == -32768) + x.i16 = 0xffffffe + assert(x.i16 == -2) + x.i16 = ffi.new("int8_t", -10) + assert(x.i16 == -10) + x.i16 = ffi.new("uint8_t", 254) + assert(x.i16 == 254) + x.i16 = ffi.new("uint16_t", 0xefa0) + assert(x.i16 == 0xefa0-65536) + x.i16 = ffi.new("int32_t", 0xffe291) + assert(x.i16 == 0xe291-65536) + x.i16 = ffi.new("int64_t", 0xffd481) + assert(x.i16 == 0xd481-65536) + + x.u16 = 99 + assert(x.u16 == 99) + x.u16 = -99 + assert(x.u16 == 65536-99) + x.u16 = 32768 + assert(x.u16 == 32768) + x.u16 = 0xffffffe + assert(x.u16 == 65534) + x.u16 = ffi.new("int8_t", -10) + assert(x.u16 == 65536-10) + x.u16 = ffi.new("uint8_t", 254) + assert(x.u16 == 254) + x.u16 = ffi.new("int16_t", 0xefa0-65536) + assert(x.u16 == 0xefa0) + x.u16 = ffi.new("int32_t", 0xffe291) + assert(x.u16 == 0xe291) + x.u16 = ffi.new("int64_t", 0xffd481) + assert(x.u16 == 0xd481) + + x.i32 = 99 + assert(x.i32 == 99) + x.i32 = -99 + assert(x.i32 == -99) + -- double to int conversion for values >= 0x80000000 is undefined + x.i32 = ffi.new("int8_t", -10) + assert(x.i32 == -10) + x.i32 = ffi.new("uint8_t", 254) + assert(x.i32 == 254) + x.i32 = ffi.new("int16_t", -517) + assert(x.i32 == -517) + x.i32 = ffi.new("uint16_t", 35876) + assert(x.i32 == 35876) + x.i32 = ffi.new("uint32_t", 0xffffe291) + assert(x.i32 == 0xffffe291-2^32) + x.i32 = ffi.new("int64_t", 15*2^32-317) + assert(x.i32 == -317) + + x.u32 = 99 + assert(x.u32 == 99) + -- x.u32 = -99 -- this is undefined on some architectures + -- assert(x.u32 == 2^32-99) + x.u32 = 0x87654321 + assert(x.u32 == 0x87654321) + x.u32 = ffi.new("int8_t", -10) + assert(x.u32 == 2^32-10) + x.u32 = ffi.new("uint8_t", 254) + assert(x.u32 == 254) + x.u32 = ffi.new("int16_t", -517) + assert(x.u32 == 2^32-517) + x.u32 = ffi.new("uint16_t", 35876) + assert(x.u32 == 35876) + x.u32 = ffi.new("int32_t", 0xffffe291-2^32) + assert(x.u32 == 0xffffe291) + x.u32 = ffi.new("int64_t", 15*2^32-317) + assert(x.u32 == 2^32-317) + + x.i64 = 99 + assert(tonumber(x.i64) == 99) + x.i64 = -99 + assert(tonumber(x.i64) == -99) + x.i64 = 0x1234*2^32+0x87654321 + assert(tonumber(x.i64) == 0x1234*2^32+0x87654321) + -- double to int64 conversion for values >= 2^63-1 is undefined + x.i64 = ffi.new("int8_t", -10) + assert(tonumber(x.i64) == -10) + x.i64 = ffi.new("uint8_t", 254) + assert(tonumber(x.i64) == 254) + x.i64 = ffi.new("int16_t", -517) + assert(tonumber(x.i64) == -517) + x.i64 = ffi.new("uint16_t", 35876) + assert(tonumber(x.i64) == 35876) + x.i64 = ffi.new("int32_t", -12345678) + assert(tonumber(x.i64) == -12345678) + x.i64 = ffi.new("uint32_t", 0xffeeddcc) + assert(tonumber(x.i64) == 0xffeeddcc) + x.i64 = ffi.new("uint64_t", 0xffeeddcc*2^32) + assert(tonumber(x.i64) == 0xffeeddcc*2^32-2^64) + + x.u64 = 99 + assert(tonumber(x.u64) == 99) + -- x.u64 = -99 -- this is undefined on some architectures + -- assert(tonumber(x.u64) == 2^64-99) + x.u64 = 0x1234*2^32+0x87654321 + assert(tonumber(x.u64) == 0x1234*2^32+0x87654321) + -- double to int64 conversion for values >= 2^63-1 is undefined + x.u64 = ffi.new("int8_t", -10) + assert(tonumber(x.u64) == 2^64-10) + x.u64 = ffi.new("uint8_t", 254) + assert(tonumber(x.u64) == 254) + x.u64 = ffi.new("int16_t", -517) + assert(tonumber(x.u64) == 2^64-517) + x.u64 = ffi.new("uint16_t", 35876) + assert(tonumber(x.u64) == 35876) + x.u64 = ffi.new("int32_t", -12345678) + assert(tonumber(x.u64) == 2^64-12345678) + x.u64 = ffi.new("uint32_t", 0xffeeddcc) + assert(tonumber(x.u64) == 0xffeeddcc) + x.u64 = ffi.new("int64_t", -0x7feeddcc*2^32) + assert(tonumber(x.u64) == 2^64-0x7feeddcc*2^32) + + -- FP to int conversions, test for truncation + x.i32 = 1.9 + assert(x.i32 == 1) + x.i32 = 2.9 + assert(x.i32 == 2) + x.i32 = -1.9 + assert(x.i32 == -1) + x.i32 = -2.9 + assert(x.i32 == -2) + x.i8 = 1.9 + assert(x.i8 == 1) + x.u8 = 1.9 + assert(x.u8 == 1) + x.i16 = 1.9 + assert(x.i16 == 1) + x.u16 = 1.9 + assert(x.u16 == 1) + x.u32 = 1.9 + assert(x.u32 == 1) + x.u64 = 1.9 + assert(tonumber(x.u64) == 1) + + -- int to FP conversions (most tested above) + x.f = ffi.new("int32_t", -17) + assert(x.f == -17) + x.d = ffi.new("int32_t", -17) + assert(x.d == -17) + -- test for rounding due to precision loss + x.f = -1717986919 + assert(x.f == -1717986944) + x.f = ffi.new("int32_t", 0x77777777) + assert(x.f == 0x77777780) + x.d = ffi.new("union { uint32_t u32[2]; uint64_t u64; }", + {{ 0x77777777, 0x77777777}}).u64 + assert(x.d == 0x77777777*2^32 + 0x77777800) + + -- complex initialization + x.cd = ffi.new("complex", 9.125, -78.5) + assert(x.cd.re == 9.125 and x.cd.im == -78.5) + x.cd = ffi.new("complex", {9.125, -78.5}) + assert(x.cd.re == 9.125 and x.cd.im == -78.5) + -- too many initializers + fails(function(x) x.cd = ffi.new("complex", 1,2,3) end, x) + + -- conversions between FP and complex + x.cf = -17.25 + assert(x.cf.re == -17.25 and x.cf.im == 0) + x.cf = ffi.new("complex float", -57.5) -- missing initializer + assert(x.cf.re == -57.5 and x.cf.im == 0) + x.cf = ffi.new("complex float", 9.125, -78.5) + assert(x.cf.re == 9.125 and x.cf.im == -78.5) + x.cf = ffi.new("complex double", 9.125, -78.5) + assert(x.cf.re == 9.125 and x.cf.im == -78.5) + + x.cd = -17.25 + assert(x.cd.re == -17.25 and x.cd.im == 0) + x.cd = ffi.new("complex double", -57.5) -- missing initializer + assert(x.cd.re == -57.5 and x.cd.im == 0) + x.cd = ffi.new("complex float", 9.125, -78.5) + assert(x.cd.re == 9.125 and x.cd.im == -78.5) + x.cd = ffi.new("complex double", 9.125, -78.5) + assert(x.cd.re == 9.125 and x.cd.im == -78.5) + + x.f = ffi.new("complex float", 9.125, -78.5) + assert(x.f == 9.125) + x.f = ffi.new("complex double", 9.125, -78.5) + assert(x.f == 9.125) + + x.d = ffi.new("complex float", 9.125, -78.5) + assert(x.d == 9.125) + x.d = ffi.new("complex double", 9.125, -78.5) + assert(x.d == 9.125) + + -- conversions between int and complex + x.cd = ffi.new("int32_t", -138) + assert(x.cd.re == -138 and x.cd.im == 0) + x.i32 = ffi.new("complex", 9.125, -78.5) + assert(x.i32 == 9) + + -- vector initialization + x.v4si = ffi.new("int __attribute__((mode(__V4SI__)))", 1, 2, 3, 4) + assert(x.v4si[0] == 1 and x.v4si[1] == 2 and + x.v4si[2] == 3 and x.v4si[3] == 4) + x.v2df = ffi.new("double __attribute__((mode(__V2DF__)))", {3.5, -6.75}) + assert(x.v2df[0] == 3.5 and x.v2df[1] == -6.75) + -- too many initializers + fails(function(x) + x.v4si = ffi.new("int __attribute__((mode(__V4SI__)))", 1,2,3,4,5) + end, x) + + -- conversions to vectors + x.v4si = -17 + assert(x.v4si[0] == -17 and x.v4si[1] == -17 and + x.v4si[2] == -17 and x.v4si[3] == -17) + x.v4si = ffi.new("int32_t", 712) + assert(x.v4si[0] == 712 and x.v4si[1] == 712 and + x.v4si[2] == 712 and x.v4si[3] == 712) + x.v2df = 12.5 + assert(x.v2df[0] == 12.5 and x.v2df[1] == 12.5) + x.v2df = ffi.new("complex", 9.125, -78.5) + assert(x.v2df[0] == 9.125 and x.v2df[1] == 9.125) + + -- assignment of same-sized but differently-typed vectors + x.v16qi = 99 + x.v4si = 0x33333333 + x.v16qi = x.v4si + assert(x.v16qi[0] == 0x33 and x.v16qi[15] == 0x33) + + -- string converted to enum + -- x.ue = -1 -- this is undefined on some architectures + -- assert(x.ue == 0xffffffff) + x.ue = "UE0" + assert(x.ue == 0) + x.ue = "UE72" + assert(x.ue == 72) + x.ie = -1 + assert(x.ie == -1) + x.ie = "IE0" + assert(x.ie == 0) + x.ie = "IEM11" + assert(x.ie == -11) + + x.pi = x.pi + -- assignment to pointer with higher qualifiers is ok + x.pci = x.pi + x.pvi = x.pi + -- assignment to pointer with lower qualifiers is not ok + fails(function(x) x.pi = x.pci end, x) + fails(function(x) x.pi = x.pvi end, x) + fails(function(x) x.pci = x.pvi end, x) + fails(function(x) x.pvi = x.pci end, x) + -- assignment of pointers with incompatible child types is not ok + fails(function(x) x.ppi = x.ai end, x) + fails(function(x) x.ppi = x.pi end, x) + fails(function(x) x.ppv = x.ppi end, x) + -- qualifiers of child types must match, higher qualifiers not ok + fails(function(x) x.ppci = x.ppi end, x) + fails(function(x) x.ppi = x.ppci end, x) + + -- pointer/int conversions are not allowed by default + fails(function(x) x.pi = 1 end, x) + fails(function(x) x.i32 = x.pi end, x) + assert(tonumber(x.pi) == nil) + assert(tonumber(x.ai) == nil) + assert(tonumber(x.si) == nil) + + -- but pointer/int casts are allowed + x.pi = ffi.cast("int *", ffi.new("int32_t", 0x12345678)) + x.i32 = ffi.cast("int32_t", x.pi) + assert(x.i32 == 0x12345678) + x.pi = ffi.cast("int *", 1234560.3) + x.i32 = ffi.cast("int32_t", x.pi) + assert(x.i32 == 1234560) + -- bad cast from non-TValue double to pointer + fails(function(x) + ffi.cast("int *", ffi.new("double", 1.5)) + end, x) + + -- nil sets a pointer to NULL + x.pi = nil + assert(tonumber(ffi.cast("uintptr_t", x.pi)) == 0) + + -- userdata and lightuserdata are treated as void * + do + local u = newproxy() + local uaddr = _G.tonumber(string.match(tostring(u), "(0x.*)")) + x.pi = u + assert(tonumber(ffi.cast("uintptr_t", x.pi)) == uaddr) + x.pi = ctest.lightud(12345678) + assert(tonumber(ffi.cast("uintptr_t", x.pi)) == 12345678) + end + + -- io.* file converts to file handle (as a void *) + if ffi.abi("win") then + assert(ffi.C._fileno(io.stdout) == 1) + assert(ffi.C._fileno(io.stderr) == 2) + local x + for i=1,100 do x = ffi.C._fileno(io.stderr) end + assert(x == 2) + else + assert(ffi.C.fileno(io.stdout) == 1) + assert(ffi.C.fileno(io.stderr) == 2) + local x + for i=1,100 do x = ffi.C.fileno(io.stderr) end + assert(x == 2) + end + + -- truncation/extension of __ptr32 + if ffi.abi("64bit") then + x.pi = ffi.cast("int *", 15*2^32+0x12345678) + assert(tonumber(ffi.cast("uintptr_t", x.pi)) == 15*2^32+0x12345678) + x.p32i = x.pi + assert(tonumber(ffi.cast("uintptr_t", x.p32i)) == 0x12345678) + x.pi = ffi.cast("int *", 0x1234*2^32+0x56780000) + x.pi = x.p32i + assert(tonumber(ffi.cast("uintptr_t", x.pi)) == 0x12345678) + end + + -- reference initialization + do + x.ai[0] = 712 + local ri = ffi.new("int &", x.ai) + assert(tonumber(ri) == 712) + local ra = ffi.new("int (&)[10]", ffi.cast("int (*)[10]", x.ai)) + assert(ra[0] == 712) + end + + -- ffi.sizeof follows references + assert(ffi.sizeof(x.ai) == 4*10) + -- ffi.offsetof follows references + assert(ffi.offsetof(x.s, "v") == 0) + assert(ffi.offsetof(x.s, "w") == 4) + + -- ffi.fill writes the right amount + ffi.fill(x.ai2, ffi.sizeof(x.ai2), 0x72) + ffi.fill(x.ai, ffi.sizeof(x.ai), 0x13) + assert(x.ai[0] == 0x13131313) + assert(x.ai[9] == 0x13131313) + assert(x.ai2[0] == 0x72727272) + assert(x.ai2[9] == 0x72727272) + + -- array cannot be assigned a pointer + fails(function(x) x.ai = x.pi end, x) + -- but pointer can be assigned the address of an array + x.pi = x.ai2 + assert(x.pi[0] == 0x72727272) + assert(x.pi[9] == 0x72727272) + x.pi = x.ai + assert(x.pi[0] == 0x13131313) + assert(x.pi[9] == 0x13131313) + x.ai = x.ai2 -- array copy + assert(x.ai[0] == 0x72727272) + assert(x.ai[9] == 0x72727272) + -- reflected via pointer, too + assert(x.pi[0] == 0x72727272) + assert(x.pi[9] == 0x72727272) + -- mismatched type or size in array copy + fails(function(x) x.ai = x.ac end, x) + fails(function(x) x.ai = ffi.new("int[20]") end, x) + fails(function(x) x.ai = ffi.new("arrinc_t").a end, x) + fails(function(x) ffi.new("arrinc_t").a = x.ai end, x) + + ffi.fill(x.s2, ffi.sizeof(x.s2), 0x59) + x.s.v = 0x12345678 + x.s.w = 0x789abcde + assert(x.s.v == 0x12345678) + assert(x.s.w == 0x789abcde) + + -- struct cannot be assigned a pointer + fails(function(x) x.s = x.ps end, x) + -- but pointer can be assigned the address of a struct + x.ps = x.s + assert(x.ps.v == 0x12345678) + assert(x.ps.w == 0x789abcde) + x.pcs = x.s + assert(x.pcs.v == 0x12345678) + assert(x.pcs.w == 0x789abcde) + x.s = x.s2 -- struct copy + assert(x.s.v == 0x59595959) + assert(x.s.w == 0x59595959) + -- reflected via pointer, too + assert(x.ps.v == 0x59595959) + assert(x.ps.w == 0x59595959) + + -- structs must be identical, structural equivalence is not enough + fails(function(x) x.ps = x.sx end, x) + fails(function(x) x.s = x.sx end, x) + + -- string copy to arrays + x.ac_guard = 99 + ffi.fill(x.ac, 10, 0x37) + x.ac = "ABCD" + assert(x.ac[0] == 65+0) + assert(x.ac[3] == 65+3) + assert(x.ac[4] == 0) + assert(x.ac[5] == 0x37) + x.ac = "ABCDEFGHI" + assert(x.ac[8] == 65+8) + assert(x.ac[9] == 0) + x.ac = "ABCDEFGHIJ" -- reduced size + assert(x.ac[8] == 65+8) + assert(x.ac[9] == 65+9) + x.ac = "ABCDEFGHIJKLM" + assert(x.ac[8] == 65+8) + assert(x.ac[9] == 65+9) + do -- copy to a[?] + local vx = ffi.new("struct { char ac[?]; }", 20) + ffi.fill(vx.ac, 20, 0x37) + vx.ac = "ABCDEFGHI" + assert(vx.ac[8] == 65+8) + assert(vx.ac[9] == 0) + end + do -- copy to a[0] + local vx = ffi.new("union { char ac[0]; char c[20]; }") + ffi.fill(vx.ac, 20, 0x37) + vx.ac = "ABCDEFGHI" + assert(vx.ac[8] == 65+8) + assert(vx.ac[9] == 0) + end + -- mismatched type or size in string copy + fails(function(x) x.i32 = "ABCD" end, x) + fails(function(x) x.ai = "ABCD" end, x) + assert(x.ac_guard == 99) -- Check guard + + -- array initialization + x.ai = ffi.new("int[10]") -- zero fill + for i=0,9 do assert(x.ai[i] == 0) end + x.ai = ffi.new("int[10]", -67) -- replicate first element + for i=0,9 do assert(x.ai[i] == -67) end + x.ai = ffi.new("int[10]", 42, -27) -- remainder filled with zero + assert(x.ai[0] == 42) + assert(x.ai[1] == -27) + for i=2,9 do assert(x.ai[i] == 0) end + x.ai = ffi.new("int[10]", 1,2,3,4,5,6,7,8,9,10) + for i=0,9 do assert(x.ai[i] == i+1) end + x.ai = ffi.new("int[10]", {1,2,3,4,5,6,7,8,9,10}) + for i=0,9 do assert(x.ai[i] == i+1) end + -- VLA initialization + do + local v = ffi.new("int[?]", 4) + for i=0,3 do assert(v[i] == 0) end + local v = ffi.new("int[?]", 4, 833) + for i=0,3 do assert(v[i] == 833) end + local v = ffi.new("int[?]", 4, 12, -9) + assert(v[0] == 12 and v[1] == -9 and v[2] == 0 and v[3] == 0) + local v = ffi.new("int[?]", 4, 1,2,3,4) + assert(v[0] == 1 and v[1] == 2 and v[2] == 3 and v[3] == 4) + end + -- too many initializers + fails(function(x) x.ai = {1,2,3,4,5,6,7,8,9,10,11} end, x) + for i=0,9 do assert(x.ai[i] == i+1) end -- but it's partially executed + fails(function(x) + local v = ffi.new("int[?]", 4, 1,2,3,4,5) + end, x) + + -- struct initialization + x.sn = ffi.new("nest_t") -- zero fill + assert(x.sn.e.e2 == 0) + x.sn = ffi.new("nest_t", 1,2) -- remainder filled with zero + assert(x.sn.a == 1 and x.sn.b == 2 and x.sn.c == 0 and x.sn.d == 0) + assert(x.sn.e.e1 == 0 and x.sn.e.e2 == 0) + assert(x.sn.f[0] == 0 and x.sn.f[1] == 0) + x.sn = ffi.new("nest_t", 1,2,3,4,{5,6},{7,8}) -- multi-value init + assert(x.sn.a == 1 and x.sn.b == 2 and x.sn.c == 3 and x.sn.d == 4) + assert(x.sn.e.e1 == 5 and x.sn.e.e2 == 6) + assert(x.sn.f[0] == 7 and x.sn.f[1] == 8) + x.sn = ffi.new("nest_t", {1,2,3,4,{5,6},{7,8}}) -- single-value init + assert(x.sn.a == 1 and x.sn.b == 2 and x.sn.c == 3 and x.sn.d == 4) + assert(x.sn.e.e1 == 5 and x.sn.e.e2 == 6) + assert(x.sn.f[0] == 7 and x.sn.f[1] == 8) + -- VLS initialization + do + local v = ffi.new("struct { int x; int a[?]; }", 4) + assert(v.x == 0) + for i=0,3 do assert(v.a[i] == 0) end + local v = ffi.new("struct { int x; int a[?]; }", 4, 9, {833}) + assert(v.x == 9) + -- NYI: fill up VLA in VLS. currently seen as indefinite length + -- for i=0,3 do assert(v.a[i] == 833) end + assert(v.a[0] == 833 and v.a[1] == 0 and v.a[2] == 0 and v.a[3] == 0) + end + -- no multi-value init beyond first level + fails(function(x) + x.sn = ffi.new("nest_t", 1,2,3,4,5,6,7,8) + end, x) + -- too many initializers + fails(function(x) + x.sn = ffi.new("nest_t", 1,2,3,4,{5,6},{7,8}, 9) + end, x) + + -- union initialization + x.ui = ffi.new("uni_t") -- zero fill + assert(x.ui.a == 0 and x.ui.b == 0 and x.ui.c == 0) + x.ui = ffi.new("uni_t", 255) -- initialize first field, remainder is zero + if ffi.abi("le") then + assert(x.ui.a == -1 and x.ui.b == 255 and x.ui.c == 255) + else + assert(x.ui.a == -1 and x.ui.b == -256 and x.ui.c == -16777216) + end + -- too many initializers + fails(function(x) + x.sn = ffi.new("uni_t", 1,2) + end, x) + fails(function() + ffi.new("union { struct { int x; }; int y; }", 1,2) + end) + + -- table converted to array + ffi.fill(x.ai, ffi.sizeof(x.ai), 0x13) + x.ai_guard = 99 + x.ai = {} -- zero fill + for i=0,9 do assert(x.ai[i] == 0) end + x.ai = {42} -- replicate + for i=0,9 do assert(x.ai[i] == 42) end + x.ai = {[0] = -67} -- replicate from index 0 + for i=0,9 do assert(x.ai[i] == -67) end + x.ai = {42, -27} -- remainder filled with zero + assert(x.ai[0] == 42) + assert(x.ai[1] == -27) + for i=2,9 do assert(x.ai[i] == 0) end + assert(x.ai_guard == 99) -- Check guard + + -- table converted to struct + ffi.fill(x.si, ffi.sizeof(x.si), 0x74) + x.si_guard = 97 + -- convert from array part + x.si = {} -- zero fill + assert(x.si.a == 0 and x.si.b == 0 and x.si.c == 0) + x.si = {42, 18} -- fill fields in order + assert(x.si.a == 42 and x.si.b == 18 and x.si.c == 0) + x.si = {[0] = -67, 12} -- fill fields in order from index 0 + assert(x.si.a == -67 and x.si.b == 12 and x.si.c == 0) + x.si = {42, -27, 19, 8} -- too many initializers ignored + assert(x.si.a == 42 and x.si.b == -27 and x.si.c == 19) + -- convert from hash part + x.si = {b = 12} + assert(x.si.a == 0 and x.si.b == 12 and x.si.c == 0) + x.si = {b = 12, c = 85, a = 35} + assert(x.si.a == 35 and x.si.b == 12 and x.si.c == 85) + x.si = {b = 19, foo = 1, bar = 2} -- unknown initializers ignored + assert(x.si.a == 0 and x.si.b == 19 and x.si.c == 0) + x.si = {b = 12, 5, 6, 7} -- hash part ignored if array part exists + assert(x.si.a == 5 and x.si.b == 6 and x.si.c == 7) + assert(x.si_guard == 97) -- Check guard + + -- table converted to struct with transparent/nested structs and arrays + ffi.fill(x.sn, ffi.sizeof(x.sn), 0x74) + x.sn = {} -- zero fill + assert(x.sn.e.e2 == 0) + x.sn = {1,2,3,4,{5,6},{7,8}} + assert(x.sn.a == 1 and x.sn.b == 2 and x.sn.c == 3 and x.sn.d == 4) + assert(x.sn.e.e1 == 5 and x.sn.e.e2 == 6) + assert(x.sn.f[0] == 7 and x.sn.f[1] == 8) + x.sn = {c = 10, e = {11,12}, f = {13,14}} + assert(x.sn.a == 0 and x.sn.b == 0 and x.sn.c == 10 and x.sn.d == 0) + assert(x.sn.e.e1 == 11 and x.sn.e.e2 == 12) + assert(x.sn.f[0] == 13 and x.sn.f[1] == 14) + + -- table converted to union + ffi.fill(x.ui, ffi.sizeof(x.ui), 0x58) + x.ui = {} -- zero fill + assert(x.ui.a == 0 and x.ui.b == 0 and x.ui.c == 0) + x.ui = {255, -1, -1} -- only first initializer used + if ffi.abi("le") then + assert(x.ui.a == -1 and x.ui.b == 255 and x.ui.c == 255) + else + assert(x.ui.a == -1 and x.ui.b == -256 and x.ui.c == -16777216) + end + x.ui = {b = -1} -- initialize a specific element of the union + if ffi.abi("le") then + assert(x.ui.a == -1 and x.ui.b == -1 and x.ui.c == 65535) + else + assert(x.ui.a == -1 and x.ui.b == -1 and x.ui.c == -65536) + end + + -- copy constructor + do + x.s.v = 1; x.s.w = 2 + local s = ffi.new("bar_t", x.s) + assert(s.v == 1 and s.w == 2) + for i=0,9 do x.ai[i] = i end + local a = ffi.new("int[10]", x.ai) + for i=0,9 do assert(a[i] == i) end + end + + -- assignment to function pointer + x.ppf = ffi.C.strcpy +end + +do + collectgarbage() + local oc = collectgarbage("count") + local cd = ffi.new"struct { struct { int a; } x;}" + local function f(cd) + local x + for i=1,1e5 do x = cd.x end + end + for i=1,2 do + f(cd) + local nc = collectgarbage("count") + assert(nc < oc + 200, "GC step missing for cdata __index") + jit.off(f) + end +end + diff --git a/test/ffi/ffi_copy_fill.lua b/test/ffi/ffi_copy_fill.lua new file mode 100644 index 0000000000..ef060bad05 --- /dev/null +++ b/test/ffi/ffi_copy_fill.lua @@ -0,0 +1,64 @@ +local ffi = require("ffi") + +do + local arr = ffi.typeof("char[11]") + local a = arr() + local b = arr() + local c = arr() + + for i=0,9 do a[i] = 97+i; b[i] = 106-i end + a[10] = 0; b[10] = 0; + + ffi.copy(c, a, 11) + for i=0,9 do assert(c[i] == 97+i) end + assert(ffi.string(c) == "abcdefghij") + + ffi.copy(c, b, 5) + for i=0,4 do assert(c[i] == 106-i) end + for i=5,9 do assert(c[i] == 97+i) end + assert(ffi.string(c) == "jihgffghij") + + c[7] = 0 + assert(ffi.string(c) == "jihgffg") + + c[10] = 1 + ffi.copy(c, "ABCDEFGHIJ") + for i=0,9 do assert(c[i] == 65+i) end + assert(c[10] == 0) + assert(ffi.string(c) == "ABCDEFGHIJ") + + ffi.copy(c, "abcdefghij", 5) + assert(ffi.string(c) == "abcdeFGHIJ") + + ffi.fill(c, 10, 65) + assert(ffi.string(c) == "AAAAAAAAAA") + for i=10,0,-1 do ffi.fill(c, i, 96+i) end + assert(ffi.string(c) == "abcdefghij") + ffi.fill(c, 10) + assert(c[0] == 0) + assert(c[9] == 0) + + -- test length parameter to ffi.string + ffi.fill(c, 10, 65) + assert(ffi.string(c, 5) == "AAAAA") +end + +do + local a = ffi.new("char[10]", 64) + local x + for i=1,100 do a[0] = i; x = ffi.string(a, 10) end + assert(x == "d@@@@@@@@@") +end + +do + local a = ffi.new("char[1]") + local x, y + for i=1,100 do + a[0] = i + x = ffi.string(a, 1) + a[0] = 126 + y = ffi.string(a, 1) + end + assert(x == "d" and y == "~") +end + diff --git a/test/ffi/ffi_enum.lua b/test/ffi/ffi_enum.lua new file mode 100644 index 0000000000..e8e40ad084 --- /dev/null +++ b/test/ffi/ffi_enum.lua @@ -0,0 +1,57 @@ + +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +ffi.cdef[[ +typedef enum enum_i { FOO_I = -1, II = 10 } enum_i; +typedef enum enum_u { FOO_U = 1, UU = 10 } enum_u; + +enum_i call_ei_i(int a) asm("call_i"); +enum_u call_eu_i(int a) asm("call_i"); +int call_i_ei(enum_i a) asm("call_i"); +int call_i_eu(enum_u a) asm("call_i"); +]] + +local C = ffi.load("../clib/ctest") + +do + + local t = ffi.new("enum_i[100]") + for i=0,99 do t[i] = "II" end + for i=0,99 do assert(t[i] == "II") end + for i=0,99 do assert(t[i] >= "II") end + for i=0,99 do t[i] = -10 end + for i=0,99 do assert(t[i] == -10) end + for i=0,99 do assert(t[i] ~= 2147483648) end + for i=1,99 do assert(t[i] == t[i-1]) end + assert(t[0]+1 == -9) + assert(t[0] ~= "BB") + fails(function() return t[0] > "BB" end) + + local u = ffi.new("enum_u[100]") + for i=0,99 do u[i] = "UU" end + for i=0,99 do assert(u[i] == "UU") end + for i=0,99 do assert(u[i] >= "UU") end + for i=0,99 do u[i] = 4294967296-10 end + for i=0,99 do assert(u[i] == 4294967296-10) end + for i=0,99 do assert(u[i] ~= -10) end + for i=1,99 do assert(u[i] == u[i-1]) end + assert(u[0]+1 == 4294967296-9) + + for i=0,99 do assert(t[i] ~= u[i]) end +end + +do + for i=0,99 do assert(C.call_ei_i(9) == "II") end + for i=0,99 do assert(C.call_eu_i(9) == "UU") end + for i=0,99 do assert(C.call_i_ei("II") == 11) end + for i=0,99 do assert(C.call_i_eu("UU") == 11) end +end + +do + local f = ffi.cast("bool (*)(enum_i)", function(e) return e == "II" end) + assert(f("II")) + assert(not f(0)) +end + diff --git a/test/ffi/ffi_err.lua b/test/ffi/ffi_err.lua new file mode 100644 index 0000000000..bd23658a2d --- /dev/null +++ b/test/ffi/ffi_err.lua @@ -0,0 +1,38 @@ +local ffi = require("ffi") + +-- error in FFI metamethod: don't print metamethod frame. +do + local ok, err = xpcall(function() + local x = (1ll).foo + end, debug.traceback) + assert(ok == false) + assert(not string.find(err, "__index")) +end + +-- tailcall in regular metamethod: keep metamethod frame. +do + local ok, err = xpcall(function() + local t = setmetatable({}, {__index = function() return rawget("x") end }) + local y = t[1] + end, debug.traceback) + assert(ok == false) + assert(string.find(err, "__index")) +end + +-- error in FFI metamethod: set correct PC. +do + ffi.cdef[[ +typedef struct { int x; int y; } point; +point strchr(point* op1, point* op2); +]] + local point = ffi.metatype("point", { __add = ffi.C.strchr }) + local function foo() + local p = point{ 3, 4 } + local r = p + p + local r = p + 5 + end + local ok, err = xpcall(foo, debug.traceback) + local line = debug.getinfo(foo).linedefined+3 + assert(string.match(err, "traceback:[^:]*:"..line..":")) +end + diff --git a/test/ffi/ffi_gcstep_recursive.lua b/test/ffi/ffi_gcstep_recursive.lua new file mode 100644 index 0000000000..cb19df1141 --- /dev/null +++ b/test/ffi/ffi_gcstep_recursive.lua @@ -0,0 +1,66 @@ +-- From Robert G. Jakabosky, 2012-03-20 + +local N=tonumber(arg[1] or 10000) + +local ffi=require"ffi" + +ffi.cdef[[ +struct Buffer { void *buf; }; +typedef struct Buffer Buffer; +]] + +local Buffer_mt = { __index = {} } +local Buffer = ffi.typeof("Buffer") + +-- used to track alive objects +local nobj_obj_flags = {} + +local function obj_to_id(ptr) + return tonumber(ffi.cast('uintptr_t', ffi.cast('void *', ptr))) +end + +function obj_type_Buffer_push(val) + local obj = Buffer(val) + local id = obj_to_id(obj) + nobj_obj_flags[id] = true + return obj +end + +local function Buffer_new(len) + local buf = ffi.cast('void *', 0xdeadbeef) + return obj_type_Buffer_push(buf) +end + +function obj_type_Buffer_delete(obj) + local id = obj_to_id(obj) + if not nobj_obj_flags[id] then return nil end + nobj_obj_flags[id] = nil + return obj.buf +end + +local getmeta = debug.getmetatable + +local function Buffer_close(self) + local buf = obj_type_Buffer_delete(self) + getmeta("Buffer_close") -- cause trace to abort + if buf then + self.buf = nil + end +end +Buffer_mt.__gc = Buffer_close +Buffer_mt.__index.close = Buffer_close + +ffi.metatype(Buffer, Buffer_mt) + +local cdata = {} +for x=1,2 do + cdata = {} + for i=1,N do + cdata[i] = Buffer_new(1) + end + for i=1,N do + cdata[i]:close() + end + cdata = nil +end + diff --git a/test/ffi/ffi_istype.lua b/test/ffi/ffi_istype.lua new file mode 100644 index 0000000000..fb538bdd4e --- /dev/null +++ b/test/ffi/ffi_istype.lua @@ -0,0 +1,89 @@ +local ffi = require("ffi") + +ffi.cdef[[ +typedef int arr_t[10]; +typedef const arr_t carr_t; +typedef struct { int x; } struct_t; +]] + +do + local void_t = ffi.typeof("void") + assert(ffi.istype(void_t, void_t)) + assert(ffi.istype("const void", void_t)) + + assert(ffi.istype("void", "void") == false) -- 2nd arg is a string. + assert(ffi.istype("double", 1.5) == false) -- 2nd arg is a number. +end + +do + local i8_t = ffi.typeof("int8_t") + local u8_t = ffi.typeof("uint8_t") + local i32_t = ffi.typeof("int32_t") + assert(ffi.istype(i32_t, i32_t) == true) + assert(ffi.istype("const int32_t", i32_t) == true) + + assert(ffi.istype("bool", u8_t) == false) + assert(ffi.istype(i8_t, u8_t) == false) + assert(ffi.istype(i32_t, u8_t) == false) + assert(ffi.istype(u8_t, i32_t) == false) + assert(ffi.istype("double", i32_t) == false) + + assert(ffi.istype("int64_t", ffi.typeof("long long"))) + assert(ffi.istype("long long", ffi.typeof("int64_t"))) +end + +do + local ptr_t = ffi.typeof("int *") + local p = ptr_t() + assert(ffi.istype(ptr_t, ptr_t) == true) + assert(ffi.istype(ptr_t, p) == true) + assert(ffi.istype(p, ptr_t) == true) + assert(ffi.istype("const int *", ptr_t) == true) + assert(ffi.istype("const int * const", ptr_t) == true) + assert(ffi.istype("unsigned int *", ptr_t) == true) + + assert(ffi.istype("char *", ptr_t) == false) + assert(ffi.istype("void *", ptr_t) == false) +end + +do + local arr_t = ffi.typeof("arr_t") + local carr_t = ffi.typeof("carr_t") + assert(ffi.istype(arr_t, arr_t) == true) + assert(ffi.istype("int[10]", arr_t) == true) + + assert(ffi.istype("int[11]", arr_t) == false) + assert(ffi.istype("int[]", arr_t) == false) + assert(ffi.istype("int *", arr_t) == false) + + assert(ffi.istype("const int[10]", arr_t) == true) + assert(ffi.istype("volatile int[10]", arr_t) == true) + assert(ffi.istype(carr_t, arr_t) == true) + + local struct_t = ffi.typeof("struct_t") + local structp_t = ffi.typeof("struct_t *") + assert(ffi.istype(struct_t, struct_t) == true) + assert(ffi.istype("const struct_t", struct_t) == true) + assert(ffi.istype("struct { int x; }", struct_t) == false) + assert(ffi.istype(struct_t, structp_t) == true) -- struct ptr is ok for struct. + assert(ffi.istype(structp_t, struct_t) == false) +end + +do + local int_t = ffi.typeof("int") + local t = {} + for i=1,200 do t[i] = int_t() end + t[100] = ffi.new("uint8_t") + local x = 0 + for i=1,200 do if not ffi.istype("int", t[i]) then x = x + i end end + assert(x == 100) + x = 0 + for i=1,200 do if not ffi.istype(int_t, t[i]) then x = x + i end end + assert(x == 100) + for i=1,200 do t[i] = int_t end + t[100] = ffi.typeof("uint8_t") + x = 0 + for i=1,200 do if not ffi.istype(t[i], int_t) then x = x + i end end + assert(x == 100) +end + diff --git a/test/ffi/ffi_jit_arith.lua b/test/ffi/ffi_jit_arith.lua new file mode 100644 index 0000000000..0554fe60aa --- /dev/null +++ b/test/ffi/ffi_jit_arith.lua @@ -0,0 +1,155 @@ +local ffi = require("ffi") + +do + local a = ffi.new("int64_t[?]", 101) + for i=1,100 do a[i] = -2 end + for i=1,100 do a[i] = i end + local x, y, m = 0ll, 0ll, 0ll + for i=1,100 do x = x + a[i]; y = y - a[i]; m = -a[i] end + assert(x == 5050) + assert(y == -5050) + assert(m == -100) + local z, z0 = 1ll, 3ll + for i=1,100 do z = a[i] * z0 end + assert(z == 300) + for i=1,100 do z = a[i] * 4ll end -- test MUL -> BSHL rule + assert(z == 400) + z, z0 = 1ll, 0x123456789abcdef0ll + for i=1,100 do z = z0 / a[i] end + assert(z == 0x123456789abcdef0ll / 100) + z, z0 = 1ll, 0x123456789abcdef0ll + for i=1,100 do z = z0 % a[i] end + assert(z == 0x123456789abcdef0ll % 100) + -- use multiple 64 bit PHIs + local t, u, v, w = 0ll, 0ll, 0ll, 0ll + for i=1,100 do t = t + a[i]; u = u + a[i]; v = v + a[i]; w = w + a[i] end + assert(t == 5050) + assert(u == 5050) + assert(v == 5050) + assert(w == 5050) +end + +do + local a = ffi.new("uint64_t[?]", 101) + for i=1,100 do a[i] = i end + local x, y, m = 0ull, 0ull, 0ull + for i=1,100 do x = x + a[i]; y = y - a[i]; m = -a[i] end + assert(x == 5050) + assert(y == 0ull-5050) + assert(m == -100ull) + local z, z0 = 1ull, 3ll + for i=1,100 do z = a[i] * z0 end + assert(z == 300) + z, z0 = 1ull, 0x123456789abcdef0ull + for i=1,100 do z = z0 / a[i] end + assert(z == 0x123456789abcdef0ull / 100) + z, z0 = 1ull, 0x123456789abcdef0ull + for i=1,100 do z = z0 % a[i] end + assert(z == 0x123456789abcdef0ull % 100) +end + +do + local x = 0ll + for i=1,100 do x = x + (-2ll) ^ (bit.band(i, 15)+1ll) end + assert(x == 262120) +end + +do + local x, a = 0ll, -2ll + for i=1,100 do x = x + a ^ (bit.band(i, 15)+1ll) end + assert(x == 262120) +end + +do + local x = 0ull + for i=1,100 do x = x + (-2ll) ^ (bit.band(i, 15)+1ull) end + assert(x == 262120) +end + +do + for i=1,200 do local j = bit.band(i, 7); assert((j == 0ll) == (j == 0)) end + for i=1,200 do assert((i < 100ll) == (i < 100)) end + for i=1,200 do assert((i <= 100ll) == (i <= 100)) end + for i=-100,100 do assert((i > 100ull) == (i < 0)) end +end + +do + local a = ffi.new("int64_t[?]", 100) + for i=0,99 do + a[i] = math.random(0, 2^32)*0x100000000LL + math.random(0, 2^32) + end + a[92] = 0x10000000LL + a[93] = 0x10000001LL + a[94] = math.random(0, 2^32) + a[95] = a[94] + 0x100000000LL + a[96] = a[94] + 0x100000001LL + a[97] = a[20] + a[98] = 0 + a[99] = -1 + + local function cksum(b) + local bxor, rol = bit.bxor, bit.rol + local x = 0 + for i=0,#b do x = rol(bxor(x, (b[i] and i or 0)), 7) end + return x + end + + local s = [[ + local a, b = ... + local k = 0 + for i=0,99 do + for j=0,99 do + b[k] = a[i] %s a[j] + k = k + 1 + end + end + ]] + + local ap = ffi.new("int64_t *", a) + local b = {} + for i=1,2 do + for _,cmp in ipairs{ "==", "~=", "<", "<=", ">", ">=" } do + local f = assert(loadstring(string.format(s, cmp), "operator"..cmp)) + f(ap, b) + local r1 = cksum(b) + jit.off(f) + f(ap, b) + local r2 = cksum(b) + assert(r1 == r2) + end + ap = ffi.new("uint64_t *", a) + end +end + +do + local a, b = ffi.new("char *"), ffi.new("char *") + local z + for i=1,100 do z = a-b end +end + +do + local x = true + local abc = ffi.cast("const char *", "abc") + for i=1,100 do x = abc == "abc" end + assert(x == true) + for i=1,100 do x = abc == "xyz" end + assert(x == false) + for i=1,100 do x = 0LL == "" end + assert(x == false) + for i=1,100 do x = 0LL == false end + assert(x == false) + for i=1,100 do x = 0LL == nil end + assert(x == false) +end + +-- ra_destpair +do + local x, y = 0, 0 + for i=1,100 do + x = x + i/3LL + y = y + i/5LL + end + assert(x == 1650) + assert(y == 970) +end + diff --git a/test/ffi/ffi_jit_array.lua b/test/ffi/ffi_jit_array.lua new file mode 100644 index 0000000000..13e26dbe33 --- /dev/null +++ b/test/ffi/ffi_jit_array.lua @@ -0,0 +1,104 @@ +local ffi = require("ffi") + +local types = { + "int8_t", "uint8_t", + "int16_t", "uint16_t", + "int32_t", "uint32_t", + "int64_t", "uint64_t", + "float", "double", +} + +for j,tp in ipairs(types) do + local t = ffi.new(tp.."[?]", 301) + for i=1,300 do t[i] = 1 end + for i=1,300 do assert(t[i] == 1) end + for i=1,300 do t[i] = t[i-1] end -- reassoc across PHIs, a[i-1] forwarding + for i=1,300 do assert(t[i] == 0) end + for i=1,300 do t[i] = i end + local x = 0 + for i=1,300 do x = x + t[i] end + if tp == "int8_t" then assert(x == 862) + elseif tp == "uint8_t" then assert(x == 33630) + else assert(x == 45150) end +end + +do + local a = ffi.new("int[?]", 101) + local p = a+1; + for i=1,100 do + p[0] = i + assert(p - a == i) -- pointer difference + p = p + 1 -- pointer increment by 4 bytes + end + for i=1,100 do assert(a[i] == i) end + for i=1,100 do assert((a+i)[0] == i) end -- pointer arithmetic + for i=1,100 do assert((i+a)[0] == i) end -- pointer arithmetic +end + +do + local a = ffi.new("double[?]", 101) + local p = a+1; + for i=1,100 do + p[0] = i + p = p + 1 -- pointer increment by 8 bytes + end + for i=1,100 do assert(a[i] == i) end + for i=1,100 do assert((a+i)[0] == i) end -- pointer arithmetic +end + +do + local a = ffi.new("double[?]", 201) + local p = a+3 + for i=1,200 do local j = bit.band(i, 7); assert((a+j == p) == (j == 3)) end + p = a+100; + for i=1,200 do assert((a+i < p) == (i < 100)) end + for i=1,200 do assert((a+i <= p) == (i <= 100)) end +end + +do + local a = ffi.new("double[?]", 100) + for i=1,100 do a[i-1LL] = i end + for i=1,100 do assert(a[100LL-i] == 101-i) end +end + +do + local a = ffi.new("int[10]") + local p = a+1 + local k = ffi.new("int", -1) + a[0] = 42 + for i=1,100 do assert(p[-1] == 42); assert(p[k] == 42) end +end + +do + local a = ffi.new("uint8_t[?]", 256) + for i=0,255 do a[i] = i end + for i=1,255 do assert(a[i] >= 1) end + for i=0,254 do assert(a[i] <= 254) end +end + +do + local a = ffi.new("int32_t[?]", 256) + local tobit, bswap, shl = bit.tobit, bit.bswap, bit.lshift + for i=0,255 do a[i] = bswap(i+0x12345600) end + for i=0,255 do assert(a[i] == tobit(shl(i, 24)+0x00563412)) end + for i=0,255 do assert(bswap(a[i]) == tobit(i+0x12345600)) end +end + +do + local a = ffi.new("int32_t[?]", 256) + local shl, shr, rol, band = bit.lshift, bit.rshift, bit.rol, bit.band + for i=0,255 do a[i] = i + shl(i, 8) + shl(i, 16) end + + for i=0,255 do assert(shl(band(a[i], 0xff), 8) == shl(i, 8)) end + for i=0,255 do assert(band(shl(a[i], 8), 0xff00) == shl(i, 8)) end + + for i=0,255 do assert(shr(band(a[i], 0xff00), 8) == i) end + for i=0,255 do assert(band(shr(a[i], 8), 255) == i) end + + for i=0,255 do assert(rol(band(a[i], 0xff), 8) == shl(i, 8)) end + for i=0,255 do assert(band(rol(a[i], 8), 0xff00) == shl(i, 8)) end + + for i=0,255 do assert(shl(band(a[i], 0x000000ff), 24) == shl(i, 24)) end + for i=0,255 do assert(shr(band(a[i], 0xffff0000), 16) == i) end +end + diff --git a/test/ffi/ffi_jit_call.lua b/test/ffi/ffi_jit_call.lua new file mode 100644 index 0000000000..b79d60b106 --- /dev/null +++ b/test/ffi/ffi_jit_call.lua @@ -0,0 +1,154 @@ + +local ffi = require("ffi") + +ffi.cdef[[ +int call_10i(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j); +double call_10d(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j); +float call_10f(float a, float b, float c, float d, float e, float f, float g, float h, float i, float j); +int64_t call_ij(int a, int64_t b); +bool call_b(int a) asm("call_i"); + +int64_t call_max(double,double,double,double,double,double,double,double,double,double,double,double,double,double,double,double,double) asm("call_10d"); + +int64_t call_10j_p(int a, int b, int c, int d, int e, int f, int g, int h, int i, const char *p) asm("call_10j"); + +int8_t call_i_i8(int a) asm("call_i"); +uint8_t call_i_u8(int a) asm("call_i"); +int16_t call_i_i16(int a) asm("call_i"); +uint16_t call_i_u16(int a) asm("call_i"); +int call_i8_i(int8_t a) asm("call_i"); +int call_u8_i(uint8_t a) asm("call_i"); +int call_i16_i(int16_t a) asm("call_i"); +int call_u16_i(uint16_t a) asm("call_i"); + +int __fastcall fastcall_void(void); +int __fastcall fastcall_i(int a); +int __fastcall fastcall_ii(int a, int b); +int __fastcall fastcall_iii(int a, int b, int c); +int64_t __fastcall fastcall_ji(int64_t a, int b); +double __fastcall fastcall_dd(double a, double b); +int __fastcall fastcall_pp_i(int *a, int *b); + +int __stdcall stdcall_i(int a); +int __stdcall stdcall_ii(int a, int b); +double __stdcall stdcall_dd(double a, double b); +float __stdcall stdcall_ff(float a, float b); +]] + +local lib = ffi.load("../clib/ctest") + +do + local x + for i=1,100 do + x = lib.call_10i(-42, 17, 12345, 9987, -100, 11, 51, 0x12345678, 338, -78901234) + end + assert(x == -42+17+12345+9987-100+11+51+0x12345678+338-78901234) +end + +do + for i=1,100 do + pcall(lib.call_max, i,i,i,i,i,i,i,i,i,i,i,i,i,i,i,i,i) + end +end + +if ffi.abi("64bit") then + local y = ffi.cast("void *", 0x123456789abcdefLL) + local x + for i=1,100 do + lib.call_10j_p(0,0,0,0,0,0,0,0,0, y) + x = lib.call_10j_p(0,0,0,0,0,0,0,0,0, nil) + end + assert(x == 0) +end + +do + local x = 0 + for i=1,100 do + x = x + lib.call_ij(100+i, i*0x300000002LL) + end + assert(x == 0x3b2e0000623eLL) +end + +do + local x + for i=1,100 do + x = lib.call_10d(-42.5, 17.125, 12345.5, 9987, -100.625, 11, 51, 0x12345678, 338, -78901234.75) + end + assert(x == -42.5+17.125+12345.5+9987-100.625+11+51+0x12345678+338-78901234.75) +end + +do + local x + for i=1,100 do + x = lib.call_10f(-42.5, 17.125, 12345.5, 9987, -100.625, 11, 51, 0x123456, 338, -789012.75) + end + assert(x == -42.5+17.125+12345.5+9987-100.625+11+51+0x123456+338-789012.75) +end + +do + local x + for i=-100,100 do + if not lib.call_b(i) then x = i end + end + assert(x == -1) + local t = {} + for i=1,100 do t[i] = -1 end + t[90] = 0 + for i=1,100 do + if lib.call_b(t[i]) then x = i end + end + assert(x == 90) +end + +do + local function tail(x) + return lib.call_b(x) + end + for i=1,100 do local a,b,c = tail(1), tail(1), tail(1) end +end + +do + local x = 0 + for i=0x01010080,0x010100ff do x = x + lib.call_i_i8(i) end + assert(x == -8128) + x = 0 + for i=0x01010080,0x010100ff do x = x + lib.call_i_u8(i) end + assert(x == 24384) + x = 0 + for i=0x0101ff80,0x0101ffff do x = x + lib.call_i_i16(i) end + assert(x == -8128) + x = 0 + for i=0x0101ff80,0x0101ffff do x = x + lib.call_i_u16(i) end + assert(x == 8314944) + x = 0 + for i=0x01010080,0x010100ff do x = x + lib.call_i8_i(i) end + assert(x == -8128) + x = 0 + for i=0x01010080,0x010100ff do x = x + lib.call_u8_i(i) end + assert(x == 24640) + x = 0 + for i=0x0101ff80,0x0101ffff do x = x + lib.call_i16_i(i) end + assert(x == -8128) + x = 0 + for i=0x0101ff80,0x0101ffff do x = x + lib.call_u16_i(i) end + assert(x == 8380480) +end + +-- target-specific +if jit.arch == "x86" then + for i=1,100 do assert(lib.fastcall_i(-42) == -41) end + for i=1,100 do assert(lib.fastcall_ii(-42, 17) == -42+17) end + for i=1,100 do assert(lib.fastcall_iii(-42, 17, 139) == -42+17+139) end + for i=1,100 do assert(lib.fastcall_ji(0x123456789LL, -17) == 0x123456789LL-17) end + for i=1,100 do assert(lib.fastcall_dd(12.5, -3.25) == 12.5-3.25) end + local x = lib.fastcall_ji + for i=1,100 do assert(x(0x123456789LL, -17) == 0x123456789LL-17) end + + if jit.os == "Windows" then + for i=1,100 do assert(lib.stdcall_i(-42) == -41) end + for i=1,100 do assert(lib.stdcall_ii(-42, 17) == -42+17) end + for i=1,100 do assert(lib.stdcall_dd(12.5, -3.25) == 12.5-3.25) end + for i=1,100 do assert(lib.stdcall_ff(12.5, -3.25) == 12.5-3.25) end + end +end + diff --git a/test/ffi/ffi_jit_complex.lua b/test/ffi/ffi_jit_complex.lua new file mode 100644 index 0000000000..4d3191cbe7 --- /dev/null +++ b/test/ffi/ffi_jit_complex.lua @@ -0,0 +1,111 @@ +local ffi = require("ffi") + +local cx = ffi.typeof("complex") +local cxf = ffi.typeof("complex float") + +ffi.cdef[[ +typedef struct cc_t { + struct cc_t *next; + complex c; +} cc_t; +]] + +do + local c = cx(1, 2) + local x + for i=1,100 do + x = c.re + c.im + end + assert(x == 3) +end + +do + local cp = ffi.new("cc_t") + local p = cp + p.next = p + p.c = cx(1, 2) + local x,y = 0,0 + for i=1,100 do + x = x + p.c.re + y = y + p.c.im + p = p.next + end + assert(x == 100) + assert(y == 200) +end + +do + local cp = ffi.new("cc_t") + local p = cp + p.next = p + p.c = cx(1, 2) + local x,y = 0,0 + for i=1,100 do + x = x + p.c[0] + y = y + p.c[1] + p = p.next + end + assert(x == 100) + assert(y == 200) +end + +do + local ca = ffi.new("complex[?]", 101) + for i=1,100 do + ca[i] = cx(i) -- handled as init single + end + local x,y = 0,0 + for i=1,100 do + x = x + ca[i].re + y = y + ca[i].im + end + assert(x == 5050) + assert(y == 0) +end + +do + local ca = ffi.new("complex[?]", 101) + for i=1,100 do + ca[i] = cx(i, -i) + end + local x,y = 0,0 + for i=1,100 do + x = x + ca[i].re + y = y + ca[i].im + end + assert(x == 5050) + assert(y == -5050) +end + +do + local ca = ffi.new("complex[?]", 101) + local caf = ffi.new("complex float[?]", 101) + for i=1,100 do + ca[i] = cxf(i, -i) + caf[i] = cx(i, -i) + end + local x,y = 0,0 + for i=1,100 do + x = x + caf[i].re + ca[i].re + y = y + caf[i].im + ca[i].im + end + assert(x == 2*5050) + assert(y == -2*5050) +end + +do + local s = ffi.new("struct { complex x;}") + for i=1,100 do + s.x = 12.5i + end + assert(s.x.re == 0) + assert(s.x.im == 12.5) +end + +-- Index overflow for complex is ignored +do + local c = cx(1, 2) + local x + for i=1e7,1e7+100 do x = c[i] end +end + diff --git a/test/ffi/ffi_jit_conv.lua b/test/ffi/ffi_jit_conv.lua new file mode 100644 index 0000000000..d4707db7a8 --- /dev/null +++ b/test/ffi/ffi_jit_conv.lua @@ -0,0 +1,277 @@ +local ffi = require("ffi") + +local ctest = require("ctest") + +do + local s = ffi.new("struct { int32_t x; }") + s.x = -0x12345678 + for i=1,100 do + s.x = s.x + 1 -- narrowed + end + assert(s.x == -0x12345678+100) +end + +do + local s = ffi.new("struct { uint32_t x; }") + s.x = 0x81234567 + for i=1,100 do + s.x = s.x + 1 -- CONV.num.u32, CONV.u32.num (no narrowing yet) + end + assert(s.x == 0x81234567+100) +end + +do + local s = ffi.new("struct { int8_t x; }") + s.x = 42 + for i=1,100 do + s.x = s.x + 1 + assert(s.x >= -128 and s.x <= 127) -- fwd -> CONV.int.i8 + end + assert(s.x == 142-256) +end + +do + local s = ffi.new("struct { uint8_t x; }") + s.x = 200 + for i=1,100 do + s.x = s.x + 1 + assert(s.x >= 0 and s.x <= 255) -- fwd -> CONV.int.u8 + end + assert(s.x == 300-256) +end + +do + local s = ffi.new("struct { int16_t x; }") + s.x = 32700 + for i=1,100 do + s.x = s.x + 1 + assert(s.x >= -32768 and s.x <= 32767) -- fwd -> CONV.int.i16 + end + assert(s.x == 32800-65536) +end + +do + local s = ffi.new("struct { uint16_t x; }") + s.x = 65450 + for i=1,100 do + s.x = s.x + 1 + assert(s.x >= 0 and s.x <= 65535) -- fwd -> CONV.int.u16 + end + assert(s.x == 65550-65536) +end + +do + local s = ffi.new("union { int32_t x; uint32_t y; }") + s.x = 0x7fffffff - 60 + local x,y = 0,0 + for i=1,100 do + if s.x == 0x7fffffff then s.x = -0x80000000 else s.x = s.x + 1 end + x = x + s.x -- fwd -> CONV.num.int + y = y + s.y -- fwd -> CONV.num.u32 + end + assert(s.x == 0x7fffffff - 60 + 100 - 2^32) + assert(s.y == 0x7fffffff - 60 + 100) + assert(y == (0x7fffffff - 60) * 100 + 5050) + assert(x == y - 40*2^32) +end + +do + local s = ffi.new("union { int32_t x; uint32_t y; }") + local x, z = 0, 2^31 + 42 + for i=1,100 do + s.y = z + x = x + s.x -- fwd -> CONV.int.u32 (dummy) + end + assert(x == 100*(-2^31 + 42)) +end + +do + local s = ffi.new("union { int8_t x; uint8_t y; }") + s.x = 42 + local x,y = 0,0 + for i=1,100 do + s.x = s.x + 1 + x = x + s.x -- fwd -> CONV.int.i8, CONV.num.int + y = y + s.y -- fwd -> CONV.int.u8, CONV.num.int + end + assert(s.x == 142 - 256) + assert(s.y == 142) + assert(y == 42 * 100 + 5050) + assert(x == y - (100-(127-42))*256) +end + +do + local a = ffi.new("uint32_t[?]", 101) + for i=1,100 do a[i] = 0x80000000+i end + local x = 0 + for i=1,100 do + x = bit.bxor(x, a[i]) -- FOLD TOBIT + CONV.num.u32 + end + assert(x == 100) +end + +do + local a = ffi.new("uint32_t[?]", 101) + for i=1,100 do a[i] = 0x80000000+i end + local x = 0 + for i=1,100 do + x = bit.bxor(a[i], 0) -- FOLD TOBIT + CONV.num.u32 + end + assert(x == -0x80000000+100) +end + +do + local v = ffi.new("float", 12.5) + local x = 0 + for i=1,100 do + x = x + tonumber(v) -- CONV.num.flt + end + assert(x == 100*12.5) +end + +do + local v = ffi.new("uint32_t", 0x80000000) + local x = 0 + for i=1,100 do + x = x + tonumber(v) -- CONV.num.u32 + end + assert(x == 100*0x80000000) +end + +do + local v = ffi.new("int64_t", 0x1234567800000000ll) + local x = 0 + for i=1,100 do + x = x + tonumber(v) -- CONV.num.i64 + end + assert(x == 100*0x12345678*2^32) +end + +do + local v = ffi.new("uint64_t", 0x89abcdef00000000ull) + local x = 0 + for i=1,100 do + x = x + tonumber(v) -- CONV.num.u64 + end + assert(x == 100*0x89abcdef*2^32) +end + +do + local a = ffi.new("int64_t[?]", 101) + for i=1,100 do a[i] = -i end + local x = 0 + for i=1,100 do + x = x + tonumber(a[i]) -- CONV.num.i64 + end + assert(x == -5050) +end + +do + local a = ffi.new("uint64_t[?]", 101) + for i=1,100 do a[i] = 2^63+2^32*i end + local x = 0 + for i=1,100 do + x = x + tonumber(a[i]) -- CONV.num.u64 + end + assert(x == 2^63*100+2^32*5050) +end + +do + local v = ffi.new("complex", 12.5, -3.25) + local x = 0 + for i=1,100 do + x = x + tonumber(v) + end + assert(x == 100*12.5) +end + +do + local s = ffi.new("struct { int64_t x;}") + for i=1,100 do + s.x = 0x123456789abcdef0LL + end + assert(tonumber(s.x) == tonumber(0x123456789abcdef0LL)) +end + +do + local s = ffi.new("struct { uint64_t x;}") + for i=1,100 do + s.x = 0x823456789abcdef0ULL + end + assert(tonumber(s.x) == tonumber(0x823456789abcdef0ULL)) +end + +do + ffi.cdef[[ + typedef enum { AA, BB, CC = -42 } foo_i; + typedef enum { DD, EE, FF = 0x80000000u } foo_u; + ]] + local s = ffi.new("struct { foo_i x; foo_u y;}") + for i=1,100 do + s.x = "CC" + assert(s.x == -42) + s.x = "BB" + assert(s.x == 1) + s.y = "FF" + assert(s.y == 0x80000000) + end + local st = ffi.typeof(s) + for i=1,100 do s = st() end + assert(s.x == 0 and s.y == 0) + for i=1,100 do s = st("CC", "EE") end + assert(s.x == -42 and s.y == 1) + local ei = ffi.new("foo_i", "CC") + local eu = ffi.new("foo_u", "EE") + for i=1,100 do s = st(ei, eu) end + assert(s.x == -42 and s.y == 1) + local x + for i=1,100 do x = tonumber(ei) end + assert(x == -42) +end + +do + local s = ffi.new("struct { const char *x; const char *y;}") + local a, tmp = "abcd", "ab" + for i=1,100 do + s.x = "abc" + s.y = string.sub(a, 1, 2) + end + assert(ffi.string(s.x) == "abc") + assert(ffi.string(s.y) == "ab") +end + +do + local s = ffi.new("struct { bool b[200]; int i[200]; double d[200];}") + for i=0,199 do s.i[i] = i-100; s.d[i] = i-100 end + for i=0,99 do s.b[i] = 0 end + for i=100,199 do s.b[i] = 1 end + for i=0,99 do assert(s.b[i] == false) end + for i=100,199 do assert(s.b[i] == true) end + for i=0,199 do s.b[i] = s.i[i] end + for i=0,199 do assert(s.b[i] == (i ~= 100)) end + for i=0,199 do s.b[i] = s.d[i] end + for i=0,199 do assert(s.b[i] == (i ~= 100)) end +end + +do + local a = ffi.new("int16_t[100]", 1) + for i=1,99 do a[i] = a[i] + a[i-1] end + assert(a[99] == 100) +end + +do + local ud = ctest.lightud(12345678) + local s = ffi.new("struct { void *p; }") + for i=1,100 do + assert(ffi.cast("uintptr_t", ud) == 12345678) + s.p = ud + end + assert(ffi.cast("uintptr_t", s.p) == 12345678) +end + +do + local x = ffi.new("struct { int & x;}", ffi.new("int[1]", 42)) + local z + for i=1,100 do z = x.x end + assert(z == 42) +end diff --git a/test/ffi/ffi_jit_misc.lua b/test/ffi/ffi_jit_misc.lua new file mode 100644 index 0000000000..800509afcd --- /dev/null +++ b/test/ffi/ffi_jit_misc.lua @@ -0,0 +1,110 @@ +local ffi = require("ffi") + +do + ffi.errno(42) + local x = 0 + for i=1,100 do x = x + ffi.errno() end + assert(x == 4200) + ffi.errno(0) +end + +do + local a = ffi.new("uint8_t[?]", 101) + for i=0,99 do a[i] = i end + local s + for i=1,90 do s = ffi.string(a+i, 10) end + assert(s == "Z[\\]^_`abc") + for i=1,90 do s = ffi.string(a+i) end + assert(s == "Z[\\]^_`abc") +end + +do + local a = ffi.new("uint8_t[?]", 100) + local x = 0 + for i=0,90 do x = x + a[i]; ffi.fill(a+i, 10, i); x = x + a[i] end + assert(x == 8100) + for i=1,100 do ffi.fill(a, 15, 0x1234) end + assert(a[0] == 0x34 and a[14] == 0x34 and a[15] == 15) + local b = ffi.new("uint32_t[?]", 104) + for i=0,100 do ffi.fill(b+i, 15, 0x1234) end + assert(b[0] == 0x34343434) + assert(b[103] == (ffi.abi("le") and 0x343434 or 0x34343400)) +end + +do + local a = ffi.new("uint8_t[?]", 100) + local b = ffi.new("uint8_t[?]", 100) + for i=0,99 do b[i] = i end + local x = 0 + for i=0,90 do x = x + a[i]; ffi.copy(a+i, b+i, 1); x = x + a[i] end + assert(x == 4095) + local x = 0 + for i=0,90 do ffi.copy(b+i, a+90-i, 10); x = x + b[i] end + assert(x == 4095) +end + +do + local a = ffi.new("uint8_t[?]", 100, 42) + for i=0,90 do ffi.copy(a+i, "abc") end + local x = 0 + for i=0,99 do x = x + a[i] end + assert(x == 9276) +end + +do + local tp = ffi.typeof("struct { int x, y; }") + local a = tp(1, 2) + local b = tp(3, 4) + local x = 0 + for i=1,100 do a.y = i; ffi.copy(b, a, 8); x = x + b.y end + assert(x == 5050) + local x = 0 + for i=1,100 do a.y = i; local t = tp(a); x = x + t.y end + assert(x == 5050) +end + +do + local tp = ffi.typeof("struct { complex x, y; }") + local cx = ffi.typeof("complex") + local a = tp(cx(1, 2), cx(3, 4)) + local x = 0 + for i=1,100 do a.y = i; local t = tp(a); x = x + t.y.re end + assert(x == 5050) +end + +do + local tp = ffi.typeof("int[10]") + local a = tp(42) + local b = ffi.new(ffi.typeof("struct { $ x; }", tp)) + for i=1,100 do b.x = a end + assert(b.x[0] == 42 and b.x[9] == 42) +end + +do + local tp = ffi.typeof("double[5]") + local a = tp(42) + local b = ffi.new(ffi.typeof("struct { $ x; }", tp)) + for i=1,100 do b.x = a end + assert(b.x[0] == 42 and b.x[4] == 42) + b.x[0] = 0 + for i=1,100 do ffi.copy(b.x, a, ffi.sizeof(a)) end + assert(b.x[0] == 42 and b.x[4] == 42) +end + +do + local x, y + for i=1,100 do x = ffi.abi("32bit"); y = ffi.abi("64bit") end + assert(x == ffi.abi("32bit")) + assert(y == ffi.abi("64bit")) + for _,s in ipairs{"64bit", "32bit", "fpu", "softfp", "hardfp", "eabi", "win", "le", "be"} do + for i=1,100 do x = ffi.abi(s) end + assert(x == ffi.abi(s)) + end +end + +do + local ct = ffi.typeof("struct { int x; }") + local cd = ct() + for i=1,100 do assert(ffi.typeof(cd) == ct) end +end + diff --git a/test/ffi/ffi_jit_struct.lua b/test/ffi/ffi_jit_struct.lua new file mode 100644 index 0000000000..e42743c90c --- /dev/null +++ b/test/ffi/ffi_jit_struct.lua @@ -0,0 +1,201 @@ +local ffi = require("ffi") + +ffi.cdef[[ +typedef struct { int a, b, c; } foo_t; +typedef struct { int a, b, c; } foo2_t; +typedef struct { int a[10]; int b[10]; } sarr_t; +typedef struct chain_t { + struct chain_t *next; + int v; +} chain_t; +]] + +do + local s = ffi.new("foo_t") + for j,k in ipairs{ "a", "b", "c" } do + for i=1,100 do s[k] = s[k] + j end + end + assert(s.a == 100) + assert(s.b == 200) + assert(s.c == 300) +end + +do + local s = ffi.new("foo_t") + for i=1,100 do + s.a = s.a + 1 + s.b = s.b + 2 + s.c = s.c + 3 + end + assert(s.a == 100) + assert(s.b == 200) + assert(s.c == 300) +end + +do + local s = ffi.new("foo_t") + local s2 = ffi.new("foo2_t", 1, 2, 3) + for i=1,100 do + s.a = s.a + s2.a + s.b = s.b + s2.b + s.c = s.c + s2.c + end + assert(s.a == 100) + assert(s.b == 200) + assert(s.c == 300) +end + +do + local s = ffi.new("sarr_t") + for i=1,100 do + s.a[5] = s.a[5] + 1 + s.b[5] = s.b[5] + 2 + end + assert(s.a[5] == 100) + assert(s.b[5] == 200) +end + +do + local s = ffi.new([[ + struct { + struct { + int x; + int b[10]; + } a[100]; + }]]) + s.a[10].b[4] = 10 + s.a[95].b[4] = 95 + local x = 0 + for i=1,100 do + x = x + s.a[i-1].b[4] -- reassociate offsets for base and index + end + assert(x == 105) +end + +do + local s1 = ffi.new("struct { int a; }") + local s2 = ffi.new("struct { int a; }") + local x = 0 + for j=1,2 do + for i=1,100 do + s2.a = i + s1.a = 1 + x = x + s2.a -- cannot forward across aliasing store + end + if j == 1 then + assert(x == 5050) + s2 = s1 + x = 0 + else + assert(x == 100) + end + end +end + +do + local s1 = ffi.new("struct { int a; }") + local s2 = ffi.new("struct { char a; }") + local x = 0 + for j=1,2 do + for i=1,100 do + s2.a = i + s1.a = 1 + x = x + s2.a -- can forward across aliasing store + end + if j == 1 then + assert(x == 5050) + s2 = s1 -- this will cause a side trace + x = 0 + else + assert(x == 100) + end + end +end + +do + local s = ffi.new("union { uint8_t a; int8_t b; }") + local x = 0 + for i=1,200 do + s.a = i + x = x + s.b -- same offset, but must not alias (except if sign-extended) + end + assert(x == 1412) +end + +do + local s1 = ffi.new("chain_t") + local s2 = ffi.new("chain_t") + local s3 = ffi.new("chain_t") + s1.next = s2 + s2.next = s3 + s3.next = s1 + local p = s1 + for i=1,99 do + p.v = i + p = p.next + end + assert(s1.v == 97) + assert(s2.v == 98) + assert(s3.v == 99) +end + +do + local ct = ffi.typeof("struct { int a,b,c; }") + local x,y,z = 0,0,0 + for i=1,100 do + local s = ct(i, i+1) + x = x + s.a + y = y + s.b + z = z + s.c + end + assert(x == 5050) + assert(y == 5150) + assert(z == 0) +end + +do + local ct = ffi.typeof("struct { double a,b,c; }") + local x,y,z = 0,0,0 + for i=1,100 do + local s = ct(i, i+1) + x = x + s.a + y = y + s.b + z = z + s.c + end + assert(x == 5050) + assert(y == 5150) + assert(z == 0) +end + +do + local s1 = ffi.new("chain_t") + local s + for i=1,100 do + s = ffi.new("chain_t", s1, i) + end + assert(tonumber(ffi.cast("int", s.next)) == + tonumber(ffi.cast("int", ffi.cast("chain_t *", s1)))) + assert(s.v == 100) +end + +do + local ct = ffi.typeof("struct { int *p; int y; }") + local s + for i=1,200 do + if i == 100 then ct = ffi.typeof("chain_t") end + s = ct(nil, 10) + end + assert(s.v == 10) +end + +do + local s = ffi.new("struct { int x; }", 42) + local function f() + for i=1,100 do + s.x = i + assert(s.x == i) + end + end + f() +end + diff --git a/test/ffi/ffi_lex_number.lua b/test/ffi/ffi_lex_number.lua new file mode 100644 index 0000000000..e26650effd --- /dev/null +++ b/test/ffi/ffi_lex_number.lua @@ -0,0 +1,51 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +local function checklex(t) + for i=1,1e9,2 do + local s = t[i+1] + if not s then break end + local s2 = assert(loadstring("return tostring("..s..")"))() + if s2 ~= t[i] then + print(s2) + error("lexer failed for '"..s.."'", 2) + end + end +end + +checklex{ + "0LL", "0ll", + "0LL", "0LL", + "0ULL", "0ull", + "0ULL", "0ULl", + "18446744073709551615ULL", "18446744073709551615llu", + "9223372036854775807LL", "0x7fffffffffffffffll", + "9223372036854775808ULL", "0x8000000000000000ull", + "1311768467463790320LL", "0x123456789abcdef0ll", + "-1LL", "-1ll", + "18446744073709551615ULL", "-1ull", + "-9223372036854775807LL", "-0x7fffffffffffffffll", + "9223372036854775808ULL", "-0x8000000000000000ull", + "0+0i", "0i", + "0+0i", "0I", + "0+12.5i", "12.5i", + "0+4660i", "0x1234i", + "0+infI", "1e400i", + "0-infI", "-1e400i", + "0-12.5i", "-12.5i", + "0-0i", "-0i", +} + +checkfail({ + "0l", + "0lll", + "0u", + "0ul", + "0ulll", + "0wll", + "0xll", + ".0ll", + "0ii", +}, function(s) assert(loadstring("return "..s)) end) + diff --git a/test/ffi/ffi_meta_tostring.lua b/test/ffi/ffi_meta_tostring.lua new file mode 100644 index 0000000000..57a34fca9d --- /dev/null +++ b/test/ffi/ffi_meta_tostring.lua @@ -0,0 +1,54 @@ +local ffi = require("ffi") + +ffi.cdef[[ +typedef union foo_t { + int64_t i64; + uint64_t u64; + complex cd; + double d[2]; + complex float cf; + float f[2]; +} foo_t; +]] + +do + local foo_t = ffi.typeof("foo_t") + local x = foo_t() + local s + + assert(tostring(foo_t) == "ctype") + assert(string.match(tostring(x), "^cdata: ")) + + assert(tostring(ffi.typeof("int (*(*[1][2])[3][4])[5][6]")) == + "ctype") + assert(tostring(ffi.typeof("int (*const)(void)")) == + "ctype") + assert(tostring(ffi.typeof("complex float(*(void))[2]")) == + "ctype") + assert(tostring(ffi.typeof("complex*")) == "ctype") + + x.i64 = -1; + assert(tostring(x.i64) == "-1LL") + assert(tostring(x.u64) == "18446744073709551615ULL") + + x.d[0] = 12.5 + x.d[1] = -753.125 + assert(tostring(x.cd) == "12.5-753.125i") + x.d[0] = -12.5 + x.d[1] = 753.125 + assert(tostring(x.cd) == "-12.5+753.125i") + x.d[0] = 0/-1 + x.d[1] = 0/-1 + assert(tostring(x.cd) == "-0-0i") + x.d[0] = 1/0 + x.d[1] = -1/0 + assert(tostring(x.cd) == "inf-infI") + x.d[0] = -1/0 + x.d[1] = 0/0 + assert(tostring(x.cd) == "-inf+nanI") + + x.f[0] = 12.5 + x.f[1] = -753.125 + assert(tostring(x.cf) == "12.5-753.125i") +end + diff --git a/test/ffi/ffi_metatype.lua b/test/ffi/ffi_metatype.lua new file mode 100644 index 0000000000..2db717f479 --- /dev/null +++ b/test/ffi/ffi_metatype.lua @@ -0,0 +1,245 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +ffi.cdef[[ +typedef struct { int x; } idx1_t; +typedef struct { int x; } idx2_t; +typedef struct { int x; } idx3_t; +typedef struct { int x,y; } arith_t; +typedef struct { void *p; } gc_t; +]] + +local function ptreq(a, b) + return ffi.cast("void *", a) == ffi.cast("void *", b) +end + +do + local nidx = {} + local tp = ffi.metatype("idx1_t", { + __index = { foo = 99, method = function(c, v) return v end }, + __newindex = nidx, + }) + + fails(function() ffi.metatype("idx1_t", {}) end) + + local s = tp(1234) + assert(s.foo == 99) + assert(s.x == 1234) + -- bad field in __index metatable + fails(function(s) local x = s.bar end, s) + assert(s:method(123) == 123) + s.bar = 42 + assert(nidx.bar == 42) + + local cs = ffi.new("const idx1_t", 9876) + assert(cs.foo == 99) + assert(cs.x == 9876) + -- write to const struct + fails(function(cs) cs.bar = 42 end, cs) + + local cp = ffi.new("const idx1_t *", cs) + assert(cp.foo == 99) + assert(cp.x == 9876) + -- write to const struct pointer + fails(function(cp) cp.bar = 42 end, cp) +end + +do + local uc, uk, uv + local tp = ffi.metatype("idx2_t", { + __index = function(c, k, x, y) + assert(x == nil and y == nil) + uc, uk = c, k; return 99 + end, + __newindex = function(c, k, v) uc, uk, uv = c, k, v end, + }) + + local s = tp(1234) + assert(s.foo == 99) + assert(ptreq(uc, s) and uk == "foo" and uv == nil); uc,uk,uv=nil,nil,nil + assert(s.x == 1234) + assert(uc == nil and uk == nil and uv == nil); uc,uk,uv=nil,nil,nil + + s.bar = 42 + assert(ptreq(uc, s) and uk == "bar" and uv == 42); uc,uk,uv=nil,nil,nil + s[10] = 11 + assert(ptreq(uc, s) and uk == 10 and uv == 11); uc,uk,uv=nil,nil,nil + + local p = ffi.new("idx2_t *", s) + assert(p.foo == 99) + assert(ptreq(uc, p) and uk == "foo" and uv == nil); uc,uk,uv=nil,nil,nil + assert(p.x == 1234) + assert(uc == nil and uk == nil and uv == nil); uc,uk,uv=nil,nil,nil + -- pointer dereference has precedence + assert(ptreq(p[0], p)) + assert(uc == nil and uk == nil and uv == nil); uc,uk,uv=nil,nil,nil + -- pointer dereference has precedence + fails(function(p) p[0] = 11 end, p) +end + +do + local uc, uk, uv + local ti, tn = {}, {} + local tp = ffi.metatype("idx3_t", { + __index = setmetatable(ti, + { __index = function(c, k) uc, uk = c, k; return 99 end }), + __newindex = setmetatable(tn, + { __newindex = function(c, k, v) uc, uk, uv = c, k, v end }), + }) + + local s = tp(1234) + assert(s.foo == 99) + assert(uc == ti and uk == "foo" and uv == nil) + uc, uk, uv = nil, nil, nil + assert(s.x == 1234) + assert(uc == nil and uk == nil and uv == nil) + + s.bar = 42 + assert(uc == tn and uk == "bar" and uv == 42) + uc, uk, uv = nil, nil, nil + s[10] = 11 + assert(uc == tn and uk == 10 and uv == 11) + uc, uk, uv = nil, nil, nil +end + +do + local tp + tp = ffi.metatype("arith_t", { + __add = function(a, b) return tp(a.x+b.x, a.y+b.y) end, + __sub = function(a, b) return tp(a.x-b.x, a.y-b.y) end, + __mul = function(a, z) return tp(a.x*z, a.y*z) end, + __div = function(z, a) return tp(a.x*z, a.y*z) end, + __concat = setmetatable({}, { __call = function(x) return 99 end }), + __len = function(x) return 2 end, + __call = function(a) return a.x+a.y end, + __tostring = function(a) return "foo" end, + __newindex = function(a, k, v) a.y = v end, + __index = { + diff = function(a) return a.x-a.y end, + }, + }) + + local a = tp(10, 20) + local b = tp(1, 2) + local c = a + b + assert(c.x == 11 and c.y == 22) + assert(c:diff() == -11) + assert(c() == 33) + local d = a - b + assert(d.x == 9 and d.y == 18) + assert(d:diff() == -9) + assert(d() == 27) + local e = a * 3 + assert(e.x == 30 and e.y == 60) + local f = 3LL / a + assert(f.x == 30 and f.y == 60) + assert(1 .. c == 99) + assert(c .. 1 == 99) + assert(c .. d == 99) + assert(tostring(c) == "foo") + assert(tostring(ffi.cast("arith_t *", c)) == "foo") + c.foo = 42 + assert(c.y == 42) + + local p = ffi.new("arith_t *", a) + local g1 = p + p + assert(g1.x == 20 and g1.y == 40) + local g2 = p[0] + p[0] + assert(g2.x == 20 and g2.y == 40) + assert(p() == 30) + + local q = ffi.new("arith_t &", a) + fails(function(p) local y = q[0] + q[0] end, q) + local h = q + q + assert(h.x == 20 and h.y == 40) + + local diff = 0 + for i=1,100 do diff = a:diff() end + assert(diff == -10) + + for i=1,100 do c.foo = i end + assert(c.y == 100) + + local z = tp(1, 3) + for i=1,100 do z = z + a end + assert(z.x == 1001 and z.y == 2003) + + local x = 0 + for i=1,100 do x = x + #a end + assert(x == 200) + + local x = 0 + for i=1,100 do x = x + p() end + assert(x == 3000) +end + +do + local count = 0 + local tp = ffi.metatype("gc_t", { + __gc = function(x) count = count + 1 end, + }) + + local a = tp() + a = nil + collectgarbage() + assert(count == 1) + local b,c = tp(), tp() + b = nil + collectgarbage() + assert(count == 2) + c = nil + collectgarbage() + assert(count == 3) + + local z + for i=1,100 do z = tp() end + z = nil + collectgarbage() + assert(count == 103) + + local t = {} + for i=1,100 do t[i] = tp() end + for i=1,100 do ffi.gc(t[i], nil) end + t = nil + collectgarbage() + assert(count == 103) +end + +do + local tp = ffi.metatype([[ +struct { + static const int Z42 = 42; + enum { Z39 = 39 }; + int x; +}]], { + __new = function(tp, x) + return ffi.new(tp, x or -1) + end, + __index = { test = function(x) return x+1 end, x = "hello" } + }) + assert(tp.Z42 == 42) + assert(tp.Z39 == 39) + assert(tp.test(99) == 100) + fails(function() tp.Z42 = 1 end) + fails(function() tp.Z39 = 1 end) + assert(tp.x == "hello") -- Not sure this is a good idea to allow that. + fails(function() tp.x = 1 end) + local o = tp() + assert(o.Z42 == 42) + assert(o.Z39 == 39) + assert(o.test(55) == 56) + fails(function() o.Z42 = 1 end) + fails(function() o.Z39 = 1 end) + assert(o.x == -1) + o.x = 5 + assert(o.x == 5) +end + +do + local fb = ffi.new("struct { int x; }", 99) + local xt = ffi.metatype("struct { }", { __index = fb }) + local o = xt() + assert(o.x == 99) +end + diff --git a/test/ffi/ffi_new.lua b/test/ffi/ffi_new.lua new file mode 100644 index 0000000000..9cdbd538cb --- /dev/null +++ b/test/ffi/ffi_new.lua @@ -0,0 +1,106 @@ +local ffi = require("ffi") +local bit = require("bit") + +dofile("../common/ffi_util.inc") + +ffi.cdef([[ +typedef struct { int a,b,c; } foo1_t; +typedef int foo2_t[?]; +void *malloc(size_t size); +void free(void *ptr); +]]) + +do + assert(ffi.sizeof("foo1_t") == 12) + local cd = ffi.new("foo1_t") + assert(ffi.sizeof(cd) == 12) + local foo1_t = ffi.typeof("foo1_t") + assert(ffi.sizeof(foo1_t) == 12) + cd = foo1_t() + assert(ffi.sizeof(cd) == 12) +end + +do + assert(ffi.sizeof("foo2_t", 3) == 12) + local cd = ffi.new("foo2_t", 3) + assert(ffi.sizeof(cd) == 12) + local foo2_t = ffi.typeof("foo2_t") + fails(ffi.sizeof, foo2_t) + assert(ffi.sizeof(foo2_t, 3) == 12) + cd = foo2_t(3) + assert(ffi.sizeof(cd) == 12) +end + +do + local tpi = ffi.typeof("int") + local tpb = ffi.typeof("uint8_t") + local t = {} + for i=1,200 do t[i] = tpi end + t[100] = tpb + local x = 0 + for i=1,200 do x = x + tonumber(ffi.new(t[i], 257)) end + assert(x == 199*257 + 1) +end + +do + local oc = collectgarbage("count") + for al=0,15 do + local align = 2^al -- 1, 2, 4, ..., 32768 + local ct = ffi.typeof("struct { char __attribute__((aligned("..align.."))) a; }") + for i=1,100 do + local cd = ct() + local addr = tonumber(ffi.cast("intptr_t", ffi.cast("void *", cd))) + assert(bit.band(addr, align-1) == 0) + end + end + local nc = collectgarbage("count") + assert(nc < oc + 3000, "GC step missing for ffi.new") +end + +do + local t = {} + for i=1,100 do t[i] = ffi.new("int[?]", i) end + assert(ffi.sizeof(t[100]) == 400) + for i=0,99 do assert(t[100][i] == 0) end +end + +do + local t = {} + local ct = ffi.typeof("struct { double x; int y[?];}") + for i=1,100 do t[i] = ct(i) end + assert(ffi.sizeof(t[100]) == 408) + for i=0,99 do assert(t[100].y[i] == 0) end +end + +do + local ct = ffi.typeof("struct __attribute__((aligned(16))) { int x; }") + local y + for i=1,200 do + local x = ct() + if i == 150 then y = x end + end + assert(bit.band(ffi.cast("intptr_t", ffi.cast("void *", y)), 15) == 0) +end + +do + local q + local p = ffi.gc(ffi.new("int[1]"), function(x) q = x end) + p = nil + collectgarbage() + assert(type(q) == "cdata") + q = nil + collectgarbage() + assert(q == nil) +end + +do + local p = ffi.gc(ffi.C.malloc(2^20), ffi.C.free) + p = nil + collectgarbage() +end + +do + local p = ffi.gc(ffi.new("int[1]"), function(x) assert(type(x) == "cdata") end) + -- test for lua_close() cleanup. +end + diff --git a/test/ffi/ffi_nosink.lua b/test/ffi/ffi_nosink.lua new file mode 100644 index 0000000000..ae51dde1a1 --- /dev/null +++ b/test/ffi/ffi_nosink.lua @@ -0,0 +1,45 @@ +local ffi = require("ffi") + +do + local x = 0ll + for i=1,100 do x=x+1; g=x end + assert(x == 100ll) + assert(g == 100ll) +end + +do + local x = 0ll + for i=1,100 do local y=x; x=x+1; g=y end + assert(x == 100ll) + assert(g == 99ll) +end + +do + local x = 0ll + local z + for i=1,100 do z=x+1; g=z end + assert(z == 1ll) + assert(g == 1ll) +end + +do + local x,y = 0ll, 0ll + for i=1,100 do y,x=x,x+1 end + assert(x == 100ll) + assert(y == 99ll) +end + +do + local st = ffi.typeof("struct { void *p; }") + local x + for i=1,100 do x = st(); x.p = x end + assert(x.p == ffi.cast("void *", x)) +end + +do + ffi.cdef[[char *strchr(char *, int);]] + for i=1,100 do + local p = ffi.new("char[2]"); + ffi.C.strchr(p, 32) + end +end diff --git a/test/ffi/ffi_parse_array.lua b/test/ffi/ffi_parse_array.lua new file mode 100644 index 0000000000..3a9616d730 --- /dev/null +++ b/test/ffi/ffi_parse_array.lua @@ -0,0 +1,78 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +checkfail{ + "int [", + "int [-1]", + "int [[1]]", + "int [10][]", + "int [10][?]", + "int [][]", + "int [][?]", + "int [?][]", + "int [?][?]", + "int [0x10000][0x2000]", + "int [256][256][256][256]", + "int [10](void)", + "int (void)[10]", + "int &[10]", + "union { double x; int a[?]; }", +} + +ffi.cdef([[ + typedef int foo1_t[10]; + typedef foo1_t foo2_t[5]; +]]) +assert(ffi.sizeof("foo1_t") == 40) +assert(ffi.sizeof("foo2_t") == 200) + +local P = ffi.sizeof("void *") + +checktypes{ + 10, 1, "char [10]", + 4*10, 4, "int [10]", + 4*10, 4, "int [10]", + 4*10*5, 4, "int [10][5]", + 4*10*5*3*2*7, 4, "int [10][5][3][2][7]", + 4*10*5, 4, "int ([10])[5]", + P*10, P, "int *[10]", + P, P, "int (*)[10]", + P*5, P, "int (*[5])[10]", + 8*10, 4, "struct { int x; char y; } [10]", + P*5*10, P, "volatile int *(* const *[5][10])(void)", + nil, 4, "int []", + 4*10, 8, "int __attribute__((aligned(8))) [10]", + 4*10, 8, "__attribute__((aligned(8))) int [10]", + 4*10, 8, "int [10] __attribute__((aligned(8)))", + 97, 1, "char ['a']", + 83, 1, "char ['\\123']", + 79, 1, "char ['\x4F']", + 5, 1, "char [sizeof(\"aa\" \"bb\")]", + 80, 8, "double [10]", +} + +do + assert(ffi.sizeof("int [?]", 10) == 4*10) + local id = ffi.typeof("const short [?]") + assert(ffi.sizeof(id, 10) == 2*10) + assert(ffi.sizeof(id, 0) == 0*10) + fails(ffi.sizeof, id) + assert(ffi.sizeof(id, -1) == nil) + assert(ffi.sizeof(id, 0x80000000) == nil) + assert(ffi.sizeof(id, 0x40000000) == nil) + assert(ffi.sizeof(id, 0x3fffffff) == 2*0x3fffffff) +end + +do + assert(ffi.sizeof("struct { double x; int a[?]; }", 10) == 8+4*10) + local id = ffi.typeof("struct { int x; short a[?]; }") + assert(ffi.sizeof(id, 10) == 4+2*10) + assert(ffi.sizeof(id, 0) == 4+0*10) + fails(ffi.sizeof, id) + assert(ffi.sizeof(id, -1) == nil) + assert(ffi.sizeof(id, 0x80000000) == nil) + assert(ffi.sizeof(id, 0x40000000) == nil) + assert(ffi.sizeof(id, 0x3ffffffd) == 4+2*0x3ffffffd) +end + diff --git a/test/ffi/ffi_parse_basic.lua b/test/ffi/ffi_parse_basic.lua new file mode 100644 index 0000000000..c054bcfb89 --- /dev/null +++ b/test/ffi/ffi_parse_basic.lua @@ -0,0 +1,131 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +checkfail{ + "", + " ", + "\n", + "1", + ".", + ";", + ",", + "*", + "[]", + "()", + "(*)", + "//", + "/*", + "xyz", + "const", + "volatile", + "typedef", + "extern", + "static", + "auto", + "register", + "struct", + "union", + "sizeof", + "int int", + "int char", + "int double", + "int;", +} + +checktypes{ + 1, 1, "char", + 1, 1, " \n\r\t\vchar \n\r\t\v", + 1, 1, "ch\\\nar", + 1, 1, "char /* abc */", + 1, 1, "char /* abc */ const", + 1, 1, "char // abc\n const", +} + +checktypes{ + nil, 1, "void", + 1, 1, "bool", + 1, 1, "_Bool", + 4, 4, "_Bool int", + 1, 1, "char", + 1, 1, "signed char", + 1, 1, "unsigned char", + 2, 2, "short", + 2, 2, "signed short", + 2, 2, "unsigned short", + 4, 4, "int", + 4, 4, "signed int", + 4, 4, "unsigned int", + 4, 4, "signed", + 4, 4, "unsigned", + 4, 4, "float", + 8, 8, "long long", + 8, 8, "signed long long", + 8, 8, "unsigned long long", + 8, 8, "double", + -- NYI: long double is architecture- and compiler-specific. + 8, 4, "_Complex float", + 16, 8, "_Complex", + 16, 8, "_Complex double", +} + +-- mode/vector_size attributes +checktypes{ + 1, 1, "int __attribute__((mode(QI)))", + 2, 2, "int __attribute__((mode(HI)))", + 4, 4, "int __attribute__((mode(SI)))", + 8, 8, "int __attribute__((mode(DI)))", + 16, 16, "int __attribute__((mode(TI)))", + 32, 16, "int __attribute__((mode(OI)))", + 4, 4, "float __attribute__((mode(SF)))", + 8, 8, "float __attribute__((mode(DF)))", + 2, 2, "int __attribute__((mode(V2QI)))", + 16, 16, "float __attribute__((mode(V4SF)))", + 32, 16, "double __attribute__((mode(V8SF)))", + 8, 8, "char __attribute__((vector_size(8)))", + 16, 16, "int __attribute__((vector_size(16)))", + 32, 16, "double __attribute__((vector_size(32)))", + 64, 16, "double __attribute__((vector_size(64)))", +} + +-- ABI-specific types: +local L = (ffi.abi("32bit") or ffi.abi("win")) and 4 or 8 +local P = ffi.abi("32bit") and 4 or 8 +local W = ffi.abi("win") and 2 or 4 + +checktypes{ + L, L, "long", + L, L, "signed long", + L, L, "unsigned long", + P, P, "int *", + P, P, "int **", + 4, 4, "int * __ptr32", +} + +checktypes{ + P, P, "ptrdiff_t", + P, P, "size_t", + W, W, "wchar_t", + 1, 1, "int8_t", + 2, 2, "int16_t", + 4, 4, "int32_t", + 8, 8, "int64_t", + 1, 1, "uint8_t", + 2, 2, "uint16_t", + 4, 4, "uint32_t", + 8, 8, "uint64_t", + P, P, "intptr_t", + P, P, "uintptr_t", +} + +checktypes{ + 1, 8, "char __attribute__((aligned(8)))", + 1, 8, "char __attribute((aligned(8)))", + 1, 8, "char __attribute__((__aligned__(8)))", + 1, 8, "__attribute__((aligned(8))) char", + 1, 8, "char __declspec(align(8))", + 1, 8, "__declspec(align(8)) char", + 1, 2, "char __attribute__((aligned(8))) const __attribute__((aligned(2)))", + 1, 16, "char __attribute__((aligned(8))) const __attribute__((aligned(16)))", +} + diff --git a/test/ffi/ffi_parse_cdef.lua b/test/ffi/ffi_parse_cdef.lua new file mode 100644 index 0000000000..4bb5d903eb --- /dev/null +++ b/test/ffi/ffi_parse_cdef.lua @@ -0,0 +1,77 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +checkfail({ + "int", + "int aa1; int aa2 ", + "static int x;", + "static const long long x = 1;", -- NYI + "static const double x = 1;", -- NYI + "static const bool x = 1;", -- NYI (intentional, need true/false) + "struct { static int x = 1; };", + ";;static int y" +}, ffi.cdef) + +ffi.cdef[[ +static const int K_42a = 42; +static const char K_42b = 42+256; +static const short K_M1a = 65535; +static const unsigned short K_65535a = 65535; +static const int K_1b = 0xffffffff >> 31; +static const int K_1c = 0xffffffffu >> 31; +static const int K_M1b = (int)0xffffffff >> 31; +]] + +checktypes{ + 42, 1, "char[K_42a]", + 42, 1, "char[K_42b]", + 1, 1, "char[-K_M1a]", + 65535, 1, "char[K_65535a]", + 1, 1, "char[K_1b]", + 1, 1, "char[K_1c]", + 1, 1, "char[-K_M1b]", +} + +ffi.cdef[[ +struct str1 { + enum { + K_99 = 99 + }; + static const int K_55 = 55; +} extk; +]] + +checktypes{ + 99, 1, "char[K_99]", + 99, 1, "char[extk.K_99]", + 99, 1, "char[((struct str1)0).K_99]", + 99, 1, "char[((struct str1 *)0)->K_99]", + 55, 1, "char[extk.K_55]", +} + +checkfail{ + "char[K_55]", +} + +ffi.cdef[[ +extern int func1(void); +extern int func2(); +static int func3(); +static inline int func4(int n) +{ + int i, k = 0; + float x = 1.0f; + for (i = 0; i < n; i++) { + k += i; + } + return k; +} +;;; +]] + +ffi.cdef[[ +int ext1; +extern int ext2; +]] + diff --git a/test/ffi/ffi_parse_struct.lua b/test/ffi/ffi_parse_struct.lua new file mode 100644 index 0000000000..16a3d053e4 --- /dev/null +++ b/test/ffi/ffi_parse_struct.lua @@ -0,0 +1,259 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +checkfail{ + "struct", + "struct {", + "struct xx xx {}", + "struct { int x }", + "struct { int x, }", + "struct { int x,y }", + "struct { void x; }", + "struct { int x(void); }", + "struct recursive1 { struct recursive1 { } x; }", + "union", + "union {", + "union xx xx {}", + "union { int x }", + "union { int x, }", + "union { int x,y }", + "union { void x; }", + "union { int x(void); }", + "union recursive1 { union recursive1 { } x; }", +} + +-- NYI: rollback doesn't recover struct state +-- ffi.cdef("struct zzz") +-- fails(ffi.cdef, "struct zzz { int") +-- ffi.cdef("struct zzz { int x; }") + +ffi.cdef("struct foo; typedef struct foo foo_t;") +assert(ffi.sizeof("struct foo") == nil) +assert(ffi.sizeof("foo_t") == nil) +ffi.cdef("struct foo { int x,y; };") +assert(ffi.sizeof("struct foo") == 8) +assert(ffi.sizeof("foo_t") == 8) +assert(ffi.sizeof(ffi.typeof("struct foo")) == 8) +assert(ffi.sizeof(ffi.typeof("foo_t")) == 8) +ffi.cdef("struct foo;") +fails(ffi.cdef, "struct foo {};") +fails(ffi.cdef, "union foo;") +fails(ffi.cdef, "union foo {};") +fails(ffi.cdef, "enum foo;") +fails(ffi.cdef, "enum foo { ZZZ1 };") + +local P = ffi.sizeof("void *") +local A = (ffi.arch == "x86" and not ffi.abi("win")) and 4 or 8 + +checktypes{ + 0, 1, "struct {}", + 1, 1, "struct { char x; }", + 2, 1, "struct { char x,y; }", + 4, 1, "struct { char x,y; char a,b; }", + 4, 2, "struct { char x; short y; }", + 4, 2, "struct { short x; char y; }", + 8, 4, "struct { char x; int y; }", + 8, 4, "struct { int x; char y; }", + 12, 4, "struct { char x; int y; char z; }", + P*4, P, "struct { char x,*y,**z,a,b,c,d; }", + 64, 4, "struct { struct { struct { struct { int x,y; } a,b; } a,b; } a,b; }", + 4, 4, "struct { struct { struct { struct { int x; }; }; }; }", + 8, 4, "struct { struct foo; }", + 8, 4, "struct { foo_t; }", + 8, 8, "struct __attribute__((aligned(sizeof(foo_t)))) { int a; }", + 6, 2, "struct { char a; char x; short y; char z; char c; }", + 10, 2, "struct { char a; struct { char x; short y; char z; } b; char c; }", + 8, A, "struct { double a; }", + A+8, A, "struct { int a; double b; }", + 8, A, "struct { long long a; }", + A+8, A, "struct { int a; long long b; }", + 16, A, "struct { _Complex a; }", + A+16, A, "struct { int a; _Complex b; }", + 8, 8, "struct { float __attribute__((mode(__V2SF__))) a; }", + 16, 8, "struct { int a; float __attribute__((mode(__V2SF__))) b; }", + 16, 8, "struct { float __attribute__((mode(__V2SF__))) a[2]; }", + 24, 8, "struct { int a; float __attribute__((mode(__V2SF__))) b[2]; }", + 16, 16, "struct { float __attribute__((vector_size(16))) a; }", + 32, 16, "struct { int a; float __attribute__((vector_size(16))) b; }", +} + +checktypes{ + 0, 1, "union {}", + 1, 1, "union { char x; }", + 1, 1, "union { char x,y; }", + 2, 2, "union { char x; short y; }", + 2, 2, "union { short x; char y; }", + 4, 4, "union { char x; int y; }", + 4, 4, "union { int x; char y; }", + 4, 4, "union { char x; int y; short z; }", + P, P, "union { char x,*y,**z,a,b,c,d; }", + 4, 4, "union { union { union { union { int x,y; } a,b; } a,b; } a,b; }", + 4, 4, "union { union { union { union { int x; }; }; }; }", + 2, 2, "union { union { short x; }; char y; }", + 2, 2, "union { struct { short x; }; char y; }", + 4, 2, "struct { union { short x; }; char y; }", + 2, 1, "union { struct { char a,b; }; char y; }", + 2, 1, "struct { union { char a,b; }; char y; }", + 8, A, "union { double a; }", + 8, A, "union { int a; double b; }", + 8, A, "union { long long a; }", + 8, A, "union { int a; long long b; }", + 16, A, "union { _Complex a; }", + 16, A, "union { int a; _Complex b; }", + 8, 8, "union { float __attribute__((mode(__V2SF__))) a; }", + 8, 8, "union { int a; float __attribute__((mode(__V2SF__))) b; }", + 16, 16, "union { float __attribute__((vector_size(16))) a; }", + 16, 16, "union { int a; float __attribute__((vector_size(16))) b; }", +} + +do + local ct + ct = ffi.typeof("struct { int a; char b; short c; int d; }") + assert(ffi.offsetof(ct, "a") == 0) + assert(ffi.offsetof(ct, "b") == 4) + assert(ffi.offsetof(ct, "c") == 6) + assert(ffi.offsetof(ct, "d") == 8) + ct = ffi.typeof("struct { char a; struct { char x; short y; char z; }; char c; }") + assert(ffi.offsetof(ct, "a") == 0) + assert(ffi.offsetof(ct, "x") == 2) + assert(ffi.offsetof(ct, "y") == 4) + assert(ffi.offsetof(ct, "z") == 6) + assert(ffi.offsetof(ct, "c") == 8) + ct = ffi.typeof("struct { char a; struct { short b; struct { int c; }; }; }") + assert(ffi.offsetof(ct, "a") == 0) + assert(ffi.offsetof(ct, "b") == 4) + assert(ffi.offsetof(ct, "c") == 8) + ct = ffi.typeof("struct { int a; double b; }") + assert(ffi.offsetof(ct, "a") == 0) + assert(ffi.offsetof(ct, "b") == A) +end + +checkfail{ + "struct { int :; }", + "struct { int a:; }", + "struct { int a:bad; }", + "struct { int a:0; }", + "struct { int a:33; }", + "struct { int a:-1; }", + "struct { _Bool a:2; }", + "struct { double a:2; }", + "struct { complex a:2; }", + "struct { int __attribute__((mode(__TI__))) a:2; }", + "struct { int __attribute__((vector_size(16))) a:2; }", + "struct { int a[2]:2; }", + "struct { void a:2; }", +} + +checktypes{ + 4, 4, "struct { unsigned a:1; }", + 4, 4, "struct { unsigned a:1, b:1, c:1; }", + 1, 1, "struct { _Bool a:1, b:1, c:1; }", + 8, 4, "struct { unsigned a:16, b:16, c:16; }", + 8, 4, "struct { unsigned a:17, b:16, c:16; }", + 12, 4, "struct { unsigned a:17, b:16, c:17; }", + 12, 4, "struct { unsigned a:16, b:17, c:16; }", + 8, 4, "struct { unsigned a:16, :16, c:16; }", + 8, 4, "struct { unsigned a:17, :16, c:16; }", + 12, 4, "struct { unsigned a:17, :16, c:17; }", + 12, 4, "struct { unsigned a:16, :17, c:16; }", + 8, 4, "struct { unsigned a:16, :0, c:16; }", + 4, 4, "struct { unsigned a:16, b:16, :0, :0; }", + 8, 4, "struct { unsigned a:16, :0, :0, :0, c:16; }", + 1, 1, "struct { char a:1; _Bool b:1; }", + 1, 1, "struct { char a:1; signed char b:1; unsigned char c:1; }", +} + +-- NYI: bit fields > 32 bit +-- local L = ffi.alignof("struct { long long a; }") +-- checktypes{ +-- L, L, "struct { long long a:1; }", +-- } + +-- Bit field packing. +checktypes{ + 1, 1, "struct { _Bool a:1, b:1, c:1; }", + 4, 4, "struct { short a:9; int b:9; char c; }", + 4, 4, "struct { char a; int b:7; }", + 4, 4, "struct { short a; char b; int c:7; }", + 4, 4, "struct { char a:7; int b:7; int c:7; int d:10; }", + 4, 1, "struct { char a:7; char b:7; char c:7; char d:7; }", + 4, 4, "struct { char a:7; int b:7, c:7, d:7; int e:4; }", + 4, 4, "struct { char a:7; int b:7, c:7, d:7; char e:4; }", + 5, 1, "struct { char a:7; char b:7, c:7, d:7; char e:4; }", + 4, 1, "struct __attribute__((packed)) { char a:7; char b:7, c:7, d:7; char e:4; }", + 4, 4, "struct { char a:7; int b:7; int c:7; int d:10; }", + 8, 4, "struct { char a:7; int b:7; char c:7; int d:10; }", + 4, 1, "struct __attribute__((packed)) { char a:7; int b:7; char c:7; int d:10; }", + 4, 1, "struct { char a:7; int b:7; char c:7; int d:10; } __attribute__((packed))", + 2, 1, "struct __attribute__((packed)) { char a:4; char b:8; }", + 2, 1, "struct __attribute__((packed)) { char a:4; char :0; char b:4; }", + 1, 1, "struct __attribute__((packed)) { _Bool a:1; _Bool b:1; }", + 2, 1, "struct __attribute__((packed)) { _Bool a:1; _Bool b:1 __attribute((aligned(1))); }", + 4, 2, "struct __attribute__((packed)) { _Bool a:1; _Bool b:1 __attribute((aligned(2))); }", + 8, 4, "struct { _Bool a:1; int b __attribute((aligned(2))); }", + 16, 8, "struct { _Bool a:1; int b __attribute((aligned(8))); }", + 6, 2, "struct { _Bool a:1; int b __attribute((aligned(2))) __attribute((packed)); }", + 6, 2, "struct __attribute__((packed)) { _Bool a:1; int b __attribute((aligned(2))); }", + 6, 2, "struct __attribute__((packed)) { _Bool a:1; int b __attribute((aligned(2))) __attribute((packed)); }", +} + +do + ffi.cdef[[ + struct foo_packorig { char a; int b; short c; }; + #pragma pack(1) + struct foo_pack1 { char a; int b; short c; }; + #pragma pack(2) + struct foo_pack2 { char a; int b; short c; }; + #pragma pack(4) + struct foo_pack4 { char a; int b; short c; }; + #pragma pack(8) + struct foo_pack8 { char a; int b; short c; }; + #pragma pack() + struct foo_packdef { char a; int b; short c; }; + #pragma pack(push) + struct foo_packpush { char a; int b; short c; }; + #pragma pack(1) + struct foo_packpush1 { char a; int b; short c; }; + #pragma pack(pop) + struct foo_packpop { char a; int b; short c; }; + #pragma pack(push,2) + struct foo_packpush2 { char a; int b; short c; }; + #pragma pack(pop) + struct foo_packpop2 { char a; int b; short c; }; + ]] + + assert(ffi.sizeof("struct foo_packorig") == 12) + assert(ffi.sizeof("struct foo_pack1") == 7) + assert(ffi.sizeof("struct foo_pack2") == 8) + assert(ffi.sizeof("struct foo_pack4") == 12) + assert(ffi.sizeof("struct foo_pack8") == 12) + assert(ffi.sizeof("struct foo_packdef") == 12) + assert(ffi.sizeof("struct foo_packpush") == 12) + assert(ffi.sizeof("struct foo_packpush1") == 7) + assert(ffi.sizeof("struct foo_packpop") == 12) + assert(ffi.sizeof("struct foo_packpush2") == 8) + assert(ffi.sizeof("struct foo_packpop2") == 12) +end + +do + ffi.cdef[[ + #pragma pack(2) + struct foo_packalign8 { + char a; int y __attribute((aligned(8))); + }; + typedef int __attribute((aligned(8))) int_align8; + struct foo_packintalign8 { + char a; int_align8 y; + }; + typedef int __attribute((aligned(1))) int_align1; + struct foo_packintalign1 { + char a; int_align1 y; + }; + ]] + + assert(ffi.sizeof("struct foo_packalign8") == 6) + assert(ffi.sizeof("struct foo_packintalign8") == 6) + assert(ffi.sizeof("struct foo_packintalign1") == 5) +end + diff --git a/test/ffi/ffi_redir.lua b/test/ffi/ffi_redir.lua new file mode 100644 index 0000000000..72e6e307bc --- /dev/null +++ b/test/ffi/ffi_redir.lua @@ -0,0 +1,22 @@ +local ffi = require("ffi") + +ffi.cdef[[ +int foo(const char *s) asm("strlen"); +]] + +assert(ffi.C.foo("abcd") == 4) + +if ffi.abi("win") then + ffi.cdef[[ + int bar asm("_fmode"); + ]] +else + ffi.cdef[[ + int bar asm("errno"); + ]] +end + +ffi.C.bar = 14 +assert(ffi.C.bar == 14) +ffi.C.bar = 0 + diff --git a/test/ffi/ffi_sink.lua b/test/ffi/ffi_sink.lua new file mode 100644 index 0000000000..ea65912764 --- /dev/null +++ b/test/ffi/ffi_sink.lua @@ -0,0 +1,122 @@ +local ffi = require("ffi") + +do + local x = 10000000000000ll + for i=1,100 do x=x+1 end + assert(x == 10000000000100ll) +end + +do + local x = 10000000000000ll + local z + for i=1,100 do z=x+1 end + assert(z == 10000000000001ll) + for i=1,100 do local y=x; z=x+1; g=y end + assert(z == 10000000000001ll) + assert(g == 10000000000000ll) +end + +do + local x = 10000000000000ll + for i=1,100 do local y=x+1; if i == 90 then x=y end end + assert(x == 10000000000001ll) +end + +do + local x = 10000000000000ll + for i=1,100 do local y=x+i; if i == 90 then x=y end end + assert(x == 10000000000090ll) +end + +do + local x = 10000000000000ll + for i=1,200 do local y=x+i; if i > 100 then x=y end end + assert(x == 10000000015050ll) +end + +do + local a = ffi.new("int[?]", 100) + local p = a + for i=0,99 do p[0]=i; p=p+1 end + assert(p == a+100) + for i=0,99 do assert(a[i] == i) end +end + +do + local cx = ffi.typeof("complex") + local x = cx(1, 2) + local k = cx(3, 4) + for i=1,100 do x = cx(x.re+k.re, x.im+k.im) end + assert(x.re == 301) + assert(x.im == 402) +end + +do + local st = ffi.typeof("struct { int a; int64_t b; double c; }") + local x = st(1, 20000000000LL, 3.5) + local k = st(3, 4, 5.0) + for i=1,100 do x = st(x.a+k.a, x.b+k.b, x.c+k.c) end + assert(x.a == 301) + assert(x.b == 20000000400LL) + assert(x.c == 503.5) + local y, z + for i=1,100 do + local x = st(i, i, i) + if i == 90 then y = st(x.a, x.b, x.c) end + x.b = x.b + 20000000000LL + if i == 95 then z = st(x.a, x.b, x.c) end + end + assert(y.a == 90) + assert(y.b == 90) + assert(y.c == 90) + assert(z.a == 95) + assert(z.b == 20000000095LL) + assert(z.c == 95) + for i=1,200 do + local x = st(i, i, i) + if i > 100 then y = st(x.a, x.b, x.c) end + x.b = x.b + 20000000000LL + if i > 150 then z = st(x.a, x.b, x.c) end + end + assert(y.a == 200) + assert(y.b == 200) + assert(y.c == 200) + assert(z.a == 200) + assert(z.b == 20000000200LL) + assert(z.c == 200) +end + +do + local st = ffi.typeof("struct { int64_t a; double b; float c; }") + local x = st(1, 2.5, 3.25) + local k = st(3, 4, 5) + for i=1,100 do x = st(x.a+k.a, x.b+k.b, x.c+k.c) end + assert(x.a == 301) + assert(x.b == 402.5) + assert(x.c == 503.25) +end + +do + local st = ffi.typeof("struct { float a; }") + local x + for i=1,200 do + local y = st(i) + if i > 100 then x = y end + end + assert(x.a == 200) +end + +do + local t = {} + for i=1,200 do t[i] = "abcd" end + local r + for i=1,200 do + local a,b,c,d + local g = t[201-i] -- Non-zero stack slot above. + local v = ffi.cast("const char *", t[i]) -- Uses 32 bit stack slot! + a,b,c,d = {v[0]},{v[1]},{v[2]},{v[3]} -- Force above to spill. + r = {{i}} -- Spill due to call. + if i > 100 then z = v[0]+a[1]+b[1]+c[1]+d[1] end -- Crash for 64 bit ptr v. + end +end + diff --git a/test/ffi/ffi_tabov.lua b/test/ffi/ffi_tabov.lua new file mode 100644 index 0000000000..ba621960bd --- /dev/null +++ b/test/ffi/ffi_tabov.lua @@ -0,0 +1,12 @@ +local ffi = require("ffi") + +local last = 0 + +assert(pcall(function() + for i=1,65536 do + last = i + ffi.typeof"struct {}" + end +end) == false) + +assert(last > 20000) diff --git a/test/ffi/ffi_type_punning.lua b/test/ffi/ffi_type_punning.lua new file mode 100644 index 0000000000..b889b52f25 --- /dev/null +++ b/test/ffi/ffi_type_punning.lua @@ -0,0 +1,147 @@ +local ffi = require("ffi") + +local u = ffi.new([[ +union { + int8_t i8[8]; + uint8_t u8[8]; + int16_t i16[4]; + uint16_t u16[4]; + int32_t i32[2]; + uint32_t u32[2]; + int64_t i64[1]; + uint64_t u64[1]; + void *v[2]; + float f[2]; + double d[1]; +} +]]) + +-- float -> u32 type punning at same offset +do + local x = 0LL + for i=1,100 do + u.f[0] = i + x = x + u.u32[0] + end + assert(x == 110888222720LL) +end + +-- double -> u64 type punning at same offset +do + local x = 0LL + for i=1,100 do + u.d[0] = i + x = x + u.u64[0] + end + assert(x == 1886586031403171840ULL) +end + +-- i8 -> u8 type punning at same offset (fwd -> CONV.int.u8) +do + local x = 0 + for i=-100,100 do + u.i8[0] = i + x = x + u.u8[0] + end + assert(x == 25600) +end + +-- p32/p64 -> u64 type punning at same offset (32 bit: different size) +do + local x = 0LL + u.u64[0] = 0 + for i=-100,150 do + u.v[0] = ffi.cast("void *", ffi.cast("ptrdiff_t", i)) + x = x + u.u64[0] + end + assert(x == (ffi.abi"64bit" and 6275ULL or + (ffi.abi"le" and 0x6400001883ULL or 0x188300000000ULL))) +end + +-- u16 -> u8 type punning at overlapping offsets +do + local x = 0 + for i=255,520 do + u.u16[0] = i + x = x + u.u8[0] + end + assert(x == (ffi.abi"be" and 274 or 32931)) +end + +do + local x = 0 + for i=255,520 do + u.u16[0] = i + x = x + u.u8[1] + end + assert(x == (ffi.abi"le" and 274 or 32931)) +end + +-- i16 -> i32 type punning at overlapping offsets +do + local x = 0 + u.i32[0] = 0 + for i=-100,150 do + u.i16[0] = i + x = x + u.i32[0] + end + assert(x == (ffi.abi"be" and 411238400 or 6559875)) +end + +do + local x = 0 + u.i32[0] = 0 + for i=-100,150 do + u.i16[1] = i + x = x + u.i32[0] + end + assert(x == (ffi.abi"le" and 411238400 or 6559875)) +end + +-- double -> i32 type punning at overlapping offsets +do + local x = 0 + for i=1.5,120,1.1 do + u.d[0] = i + x = x + u.i32[0] + end + assert(x == (ffi.abi"be" and 116468870297 or -858993573)) +end + +do + local x = 0 + for i=1.5,120,1.1 do + u.d[0] = i + x = x + u.i32[1] + end + assert(x == (ffi.abi"le" and 116468870297 or -858993573)) +end + +-- u32 -> u64 type punning, constify u, 32 bit SPLIT: fold KPTR +do + local u = ffi.new("union { struct { uint32_t lo, hi; }; uint64_t u64; }") + + local function conv(lo, hi) + u.lo = lo + u.hi = hi + return u.u64 + end + + local x = 0ll + for i=1,100 do + x = x + conv(i, i) + end + assert(x == 21689584849850ULL) +end + +-- u64 -> u32 -> u64 type punning with KPTR +do + local s = ffi.new("union { int64_t q; int32_t i[2]; }") + local function f() + s.q = 0 + s.i[1] = 1 + return s.q + end + for i=1,50 do f() f() f() end + assert(f() ~= 0) +end diff --git a/test/misc/ack.lua b/test/misc/ack.lua new file mode 100644 index 0000000000..bcc1316e1e --- /dev/null +++ b/test/misc/ack.lua @@ -0,0 +1,12 @@ +local function Ack(m, n) + if m == 0 then return n+1 end + if n == 0 then return Ack(m-1, 1) end + return Ack(m-1, (Ack(m, n-1))) -- The parentheses are deliberate. +end + +if arg and arg[1] then + local N = tonumber(arg and arg[1]) + io.write("Ack(3,", N ,"): ", Ack(3,N), "\n") +else + assert(Ack(3,5) == 253) +end diff --git a/test/misc/ack_notail.lua b/test/misc/ack_notail.lua new file mode 100644 index 0000000000..e14c003afd --- /dev/null +++ b/test/misc/ack_notail.lua @@ -0,0 +1,12 @@ +local function Ack(m, n) + if m == 0 then return n+1 end + if n == 0 then return (Ack(m-1, 1)) end + return (Ack(m-1, (Ack(m, n-1)))) -- The parentheses are deliberate. +end + +if arg and arg[1] then + local N = tonumber(arg and arg[1]) + io.write("Ack(3,", N ,"): ", Ack(3,N), "\n") +else + assert(Ack(3,5) == 253) +end diff --git a/test/misc/alias_alloc.lua b/test/misc/alias_alloc.lua new file mode 100644 index 0000000000..02fe618d81 --- /dev/null +++ b/test/misc/alias_alloc.lua @@ -0,0 +1,54 @@ + +do + local t = {1} + local x + for i=1,100 do + local v = {i} + t[1] = v[1] + x = v[1] + end + assert(x == 100 and t[1] == 100) +end + +do + local t = {1} + local x,y + for i=1,100 do + local v = {i} + local w = {i+1} + x = v[1] + y = w[1] + end + assert(x == 100 and y == 101) +end + +do + local mt = {} + local t = setmetatable({}, mt) + local x + for i=1,100 do + local v = {} + setmetatable(v, getmetatable(t)) + assert(getmetatable(v) == mt) + end +end + +-- See also sink_alloc.lua +do + local x,k={1,2},{3,4} + for i=1,100 do x = {x[1]+k[1], x[2]+k[2]} end + assert(x[1] == 301) + assert(x[2] == 402) +end + +-- FLOAD for tab.asize/tab.array crossing NEWREF. +do + local t = {1} + for i=1,100 do + local v = {} + local w = {} + v[1] = t[1] + w[1] = t[1] + end +end + diff --git a/test/misc/api_call.lua b/test/misc/api_call.lua new file mode 100644 index 0000000000..7dbd5e4011 --- /dev/null +++ b/test/misc/api_call.lua @@ -0,0 +1,98 @@ +local ctest = require("ctest") + +local function ret0() end +local function ret1() return 1 end +local function ret2() return 1,2 end +local function ret3() return 1,2,3 end +local function retva(...) return ... end +local function ret1va(...) return 1,... end + +local function pack(...) + return { n = select('#', ...), ... } +end + +local function ck(res, ...) + local ok = pack(...) + if res.n ~= ok.n then error("nresults wrong: "..res.n.." ~= "..ok.n, 2) end + for i=1,res.n do + if res[i] ~= ok[i] then + error("result["..i.."] wrong: "..tostring(res[i]).." ~= "..tostring(ok[i]), 2) + end + end +end + +local function test_adjust_results(testfunc) + + local function cc(nres, f, ...) + return pack(testfunc(nres, f, ...)) + end + + ck(cc(0, ret0)) + ck(cc(0, ret1)) + ck(cc(0, ret2)) + ck(cc(0, ret3)) + ck(cc(0, retva)) + + ck(cc(1, ret0), nil) + ck(cc(1, ret1), 1) + ck(cc(1, ret2), 1) + ck(cc(1, ret3), 1) + ck(cc(1, retva), nil) + ck(cc(1, retva, 1), 1) + + ck(cc(2, ret0), nil, nil) + ck(cc(2, ret1), 1, nil) + ck(cc(2, ret2), 1, 2) + ck(cc(2, ret3), 1, 2) + ck(cc(2, retva), nil, nil) + ck(cc(2, retva, 1), 1, nil) + ck(cc(2, retva, 1, 2), 1, 2) + + ck(cc(-1, ret0)) + ck(cc(-1, ret1), 1) + ck(cc(-1, ret2), 1, 2) + ck(cc(-1, ret3), 1, 2, 3) + ck(cc(-1, retva)) + ck(cc(-1, retva, 1), 1) + ck(cc(-1, retva, 1, 2), 1, 2) +end + +test_adjust_results(ctest.call) +test_adjust_results(ctest.pcall_err) + + +local function gcshrink() + for i=1,10 do collectgarbage() end +end + +assert(select('#', ctest.call(2000, gcshrink)) == 2000) +gcshrink() +assert(select('#', ctest.call(7000, gcshrink)) == 7000) +gcshrink() + +local function test_yield(resume, yield) + local function inpcall() + ck(pack(yield(6, 7)), 18, 19) + end + local co = coroutine.create(function(...) + ck(pack(...), 11, 12) + ck(pack(yield(1, 2))) + ck(pack(yield()), 13, 14, 15) + ck(pack(yield(3, 4, 5)), 16, 17) + assert(pcall(inpcall) == true) + return 8, 9 + end) + + ck(pack(resume(co, 11, 12)), true, 1, 2) + ck(pack(resume(co)), true) + ck(pack(resume(co, 13, 14, 15)), true, 3, 4, 5) + ck(pack(resume(co, 16, 17)), true, 6, 7) + ck(pack(resume(co, 18, 19)), true, 8, 9) + assert(resume(co) == false) +end + +test_yield(coroutine.resume, coroutine.yield) +test_yield(ctest.resume, coroutine.yield) +test_yield(coroutine.resume, ctest.yield) +test_yield(ctest.resume, ctest.yield) + diff --git a/test/misc/argcheck.lua b/test/misc/argcheck.lua new file mode 100644 index 0000000000..de3cb70ad7 --- /dev/null +++ b/test/misc/argcheck.lua @@ -0,0 +1,40 @@ + +local function check(f, msg) + local ok, err = pcall(f) + if ok then error("error check unexpectedly succeeded", 2) end + if type(err) ~= "string" then + error("error check failed with "..tostring(err), 2) + end + local line, err2 = string.match(err, ":(%d*): (.*)") + if err2 ~= msg then error("error check failed with "..err, 2) end +end + +assert(math.abs(-1.5) == 1.5) +assert(math.abs("-1.5") == 1.5) + +check(function() math.abs() end, + "bad argument #1 to 'abs' (number expected, got no value)") +check(function() math.abs(false) end, + "bad argument #1 to 'abs' (number expected, got boolean)") +check(function() math.abs("a") end, + "bad argument #1 to 'abs' (number expected, got string)") +string.abs = math.abs +check(function() ("a"):abs() end, + "calling 'abs' on bad self (number expected, got string)") + +assert(string.len("abc") == 3) +assert(string.len(123) == 3) + +check(function() string.len() end, + "bad argument #1 to 'len' (string expected, got nil)") +check(function() string.len(false) end, + "bad argument #1 to 'len' (string expected, got boolean)") + +assert(string.sub("abc", 2) == "bc") +assert(string.sub(123, "2") == "23") + +check(function() string.sub("abc", false) end, + "bad argument #2 to 'sub' (number expected, got boolean)") +check(function() ("abc"):sub(false) end, + "bad argument #1 to 'sub' (number expected, got boolean)") + diff --git a/test/misc/assign_tset_prevnil.lua b/test/misc/assign_tset_prevnil.lua new file mode 100644 index 0000000000..202153c612 --- /dev/null +++ b/test/misc/assign_tset_prevnil.lua @@ -0,0 +1,11 @@ +a, b, c = 0, 1 +assert(a == 0) +assert(b == 1) +assert(c == nil) +a, b = a+1, b+1, a+b +assert(a == 1) +assert(b == 2) +a, b, c = 0 +assert(a == 0) +assert(b == nil) +assert(c == nil) diff --git a/test/misc/assign_tset_tmp.lua b/test/misc/assign_tset_tmp.lua new file mode 100644 index 0000000000..909d5aadaa --- /dev/null +++ b/test/misc/assign_tset_tmp.lua @@ -0,0 +1,5 @@ +a = {} +i = 3 +i, a[i] = i+1, 20 +assert(i == 4) +assert(a[3] == 20) diff --git a/test/misc/bit_op.lua b/test/misc/bit_op.lua new file mode 100644 index 0000000000..4bab240c03 --- /dev/null +++ b/test/misc/bit_op.lua @@ -0,0 +1,90 @@ +-- Test cases for bit operations library. Public domain. + +local bit = require"bit" + +local vb = { + 0, 1, -1, 2, -2, 0x12345678, 0x87654321, + 0x33333333, 0x77777777, 0x55aa55aa, 0xaa55aa55, + 0x7fffffff, 0x80000000, 0xffffffff +} + +local function cksum(name, s, r) + local z = 0 + for i=1,#s do z = (z + string.byte(s, i)*i) % 2147483629 end + if z ~= r then + error("bit."..name.." test failed (got "..z..", expected "..r..")", 0) + end +end + +local function check_unop(name, r) + local f = bit[name] + local s = "" + if pcall(f) or pcall(f, "z") or pcall(f, true) then + error("bit."..name.." fails to detect argument errors", 0) + end + for _,x in ipairs(vb) do s = s..","..tostring(f(x)) end + cksum(name, s, r) +end + +local function check_binop(name, r) + local f = bit[name] + local s = "" + if pcall(f) or pcall(f, "z") or pcall(f, true) then + error("bit."..name.." fails to detect argument errors", 0) + end + for _,x in ipairs(vb) do + for _,y in ipairs(vb) do s = s..","..tostring(f(x, y)) end + end + cksum(name, s, r) +end + +local function check_binop_range(name, r, yb, ye) + local f = bit[name] + local s = "" + if pcall(f) or pcall(f, "z") or pcall(f, true) or pcall(f, 1, true) then + error("bit."..name.." fails to detect argument errors", 0) + end + for _,x in ipairs(vb) do + for y=yb,ye do s = s..","..tostring(f(x, y)) end + end + cksum(name, s, r) +end + +local function check_shift(name, r) + check_binop_range(name, r, 0, 31) +end + +-- Minimal sanity checks. +assert(0x7fffffff == 2147483647, "broken hex literals") +assert(0xffffffff == -1 or 0xffffffff == 2^32-1, "broken hex literals") +assert(tostring(-1) == "-1", "broken tostring()") +assert(tostring(0xffffffff) == "-1" or tostring(0xffffffff) == "4294967295", "broken tostring()") + +-- Basic argument processing. +assert(bit.tobit(1) == 1) +assert(bit.band(1) == 1) +assert(bit.bxor(1,2) == 3) +assert(bit.bor(1,2,4,8,16,32,64,128) == 255) + +-- Apply operations to test vectors and compare checksums. +check_unop("tobit", 277312) +check_unop("bnot", 287870) +check_unop("bswap", 307611) + +check_binop("band", 41206764) +check_binop("bor", 51253663) +check_binop("bxor", 79322427) + +check_shift("lshift", 325260344) +check_shift("rshift", 139061800) +check_shift("arshift", 111364720) +check_shift("rol", 302401155) +check_shift("ror", 302316761) + +check_binop_range("tohex", 47880306, -8, 8) + +-- Don't propagate TOBIT narrowing across two conversions. +local tobit = bit.tobit +local k = 0x8000000000003 +for i=1,100 do assert(tobit(k % (2^32)) == 3) end + diff --git a/test/misc/cat_jit.lua b/test/misc/cat_jit.lua new file mode 100644 index 0000000000..858a15bf45 --- /dev/null +++ b/test/misc/cat_jit.lua @@ -0,0 +1,113 @@ + +-- Constant folding +do + local y + for i=1,100 do y = "a".."b" end + assert(y == "ab") + for i=1,100 do y = "ab"..(1).."cd"..(1.5) end + assert(y == "ab1cd1.5") +end + +-- Fuse conversions to strings +do + local y + local x = "a" + for i=1,100 do y = x..i end + assert(y == "a100") + x = "a" + for i=1.5,100.5 do y = x..i end + assert(y == "a100.5") +end + +-- Fuse string construction +do + local y + local x = "abc" + for i=1,100 do y = "x"..string.sub(x, 2) end + assert(y == "xbc") +end + +-- CSE, sink +do + local y + local x = "a" + for i=1,100 do y = x.."b" end + assert(y == "ab") +end + +-- CSE, two buffers in parallel, no sink +do + local y, z + local x1, x2 = "xx", "yy" + for i=1,100 do y = x1.."a"..x1; z = x1.."a"..x2 end + assert(y == "xxaxx") + assert(z == "xxayy") + x1 = "xx" + for i=1,100 do y = x1.."a"..x1; z = x1.."b"..x1 end + assert(y == "xxaxx") + assert(z == "xxbxx") +end + +-- Append, CSE +do + local y, z + local x = "a" + for i=1,100 do + y = x.."b" + y = y.."c" + end + assert(y == "abc") + x = "a" + for i=1,100 do + y = x.."b" + z = y.."c" + end + assert(y == "ab") + assert(z == "abc") + x = "a" + for i=1,100 do + y = x.."b" + z = y..i + end + assert(y == "ab") + assert(z == "ab100") +end + +-- Append, FOLD +do + local a, b = "x" + for i=1,100 do b = (a.."y").."" end + assert(b == "xy") +end + +-- Append to buffer, sink +do + local x = "a" + for i=1,100 do x = x.."b" end + assert(x == "a"..string.rep("b", 100)) + x = "a" + for i=1,100 do x = x.."bc" end + assert(x == "a"..string.rep("bc", 100)) +end + +-- Append to two buffers in parallel, no append, no sink +do + local y, z = "xx", "yy" + for i=1,100 do y = y.."a"; z = z.."b" end + assert(y == "xx"..string.rep("a", 100)) + assert(z == "yy"..string.rep("b", 100)) +end + +-- Sink into side-exit +do + local x = "a" + local z + for i=1,200 do + local y = x.."b" + if i > 100 then + z = y..i + end + end + assert(z == "ab200") +end + diff --git a/test/misc/catch_wrap.lua b/test/misc/catch_wrap.lua new file mode 100644 index 0000000000..7f656bcc26 --- /dev/null +++ b/test/misc/catch_wrap.lua @@ -0,0 +1,45 @@ + +local cp = require("cpptest") +cp.wrapon() + +do + local a, b = pcall(cp.catch, function() return "x" end) + assert(a == true and b == "x") +end + +do + local a, b = pcall(function() cp.throw("foo") end) + assert(a == false and b == "foo") +end + +local unwind +do + local a, b = pcall(cp.catch, function() cp.throw("foo") end) + unwind = a + assert((a == false and b == "foo") or (a == true and b == "catch ...")) +end + +do + local st = cp.alloc(function() return cp.isalloc() end) + assert(st == true) + assert(cp.isalloc() == false) +end + +do + local a, b = pcall(cp.alloc, function() + assert(cp.isalloc() == true) + return "foo", cp.throw + end) + assert(a == false and b == "foo") + assert(cp.isalloc() == false) +end + +do + local a, b = pcall(cp.alloc, function() + assert(cp.isalloc() == true) + return "foo", error + end) + assert(a == false and b == "foo") + if unwind then assert(cp.isalloc() == false) end +end + diff --git a/test/misc/compare.lua b/test/misc/compare.lua new file mode 100644 index 0000000000..96a52d90d2 --- /dev/null +++ b/test/misc/compare.lua @@ -0,0 +1,232 @@ +local function lt(x, y) + if x < y then return true else return false end +end + +local function le(x, y) + if x <= y then return true else return false end +end + +local function gt(x, y) + if x > y then return true else return false end +end + +local function ge(x, y) + if x >= y then return true else return false end +end + +local function eq(x, y) + if x == y then return true else return false end +end + +local function ne(x, y) + if x ~= y then return true else return false end +end + + +local function ltx1(x) + if x < 1 then return true else return false end +end + +local function lex1(x) + if x <= 1 then return true else return false end +end + +local function gtx1(x) + if x > 1 then return true else return false end +end + +local function gex1(x) + if x >= 1 then return true else return false end +end + +local function eqx1(x) + if x == 1 then return true else return false end +end + +local function nex1(x) + if x ~= 1 then return true else return false end +end + + +local function lt1x(x) + if 1 < x then return true else return false end +end + +local function le1x(x) + if 1 <= x then return true else return false end +end + +local function gt1x(x) + if 1 > x then return true else return false end +end + +local function ge1x(x) + if 1 >= x then return true else return false end +end + +local function eq1x(x) + if 1 == x then return true else return false end +end + +local function ne1x(x) + if 1 ~= x then return true else return false end +end + + +local function check(a, b) + if a ~= b then + error("check failed with "..tostring(a).." ~= "..tostring(b), 2) + end +end + +local x,y = 1,2 + +check(xy, false) +check(x>=y, false) +check(x==y, false) +check(x~=y, true) + +check(1y, false) +check(1>=y, false) +check(1==y, false) +check(1~=y, true) + +check(x<2, true) +check(x<=2, true) +check(x>2, false) +check(x>=2, false) +check(x==2, false) +check(x~=2, true) + +check(lt(x,y), true) +check(le(x,y), true) +check(gt(x,y), false) +check(ge(x,y), false) +check(eq(y,x), false) +check(ne(y,x), true) + +local x,y = 2,1 + +check(xy, true) +check(x>=y, true) +check(x==y, false) +check(x~=y, true) + +check(2y, true) +check(2>=y, true) +check(2==y, false) +check(2~=y, true) + +check(x<1, false) +check(x<=1, false) +check(x>1, true) +check(x>=1, true) +check(x==1, false) +check(x~=1, true) + +check(lt(x,y), false) +check(le(x,y), false) +check(gt(x,y), true) +check(ge(x,y), true) +check(eq(y,x), false) +check(ne(y,x), true) + +local x,y = 1,1 + +check(xy, false) +check(x>=y, true) +check(x==y, true) +check(x~=y, false) + +check(1y, false) +check(1>=y, true) +check(1==y, true) +check(1~=y, false) + +check(x<1, false) +check(x<=1, true) +check(x>1, false) +check(x>=1, true) +check(x==1, true) +check(x~=1, false) + +check(lt(x,y), false) +check(le(x,y), true) +check(gt(x,y), false) +check(ge(x,y), true) +check(eq(y,x), true) +check(ne(y,x), false) + + +check(lt1x(2), true) +check(le1x(2), true) +check(gt1x(2), false) +check(ge1x(2), false) +check(eq1x(2), false) +check(ne1x(2), true) + +check(ltx1(2), false) +check(lex1(2), false) +check(gtx1(2), true) +check(gex1(2), true) +check(eqx1(2), false) +check(nex1(2), true) + + +check(lt1x(1), false) +check(le1x(1), true) +check(gt1x(1), false) +check(ge1x(1), true) +check(eq1x(1), true) +check(ne1x(1), false) + +check(ltx1(1), false) +check(lex1(1), true) +check(gtx1(1), false) +check(gex1(1), true) +check(eqx1(1), true) +check(nex1(1), false) + + +check(lt1x(0), false) +check(le1x(0), false) +check(gt1x(0), true) +check(ge1x(0), true) +check(eq1x(0), false) +check(ne1x(0), true) + +check(ltx1(0), true) +check(lex1(0), true) +check(gtx1(0), false) +check(gex1(0), false) +check(eqx1(0), false) +check(nex1(0), true) + +do + assert(not pcall(function() + local a, b = 10.5, nil + return a < b + end)) +end + +do + for i=1,100 do + assert(bit.tobit(i+0x7fffffff) < 0) + end + for i=1,100 do + assert(bit.tobit(i+0x7fffffff) <= 0) + end +end + diff --git a/test/misc/constov.lua b/test/misc/constov.lua new file mode 100644 index 0000000000..341cf32930 --- /dev/null +++ b/test/misc/constov.lua @@ -0,0 +1,19 @@ + +if not os.getenv("SLOWTEST") then return end + +do + local t = { "local x\n" } + for i=2,65537 do t[i] = "x="..i..".5\n" end + assert(loadstring(table.concat(t)) ~= nil) + t[65538] = "x=65538.5" + assert(loadstring(table.concat(t)) == nil) +end + +do + local t = { "local x\n" } + for i=2,65537 do t[i] = "x='"..i.."'\n" end + assert(loadstring(table.concat(t)) ~= nil) + t[65538] = "x='65538'" + assert(loadstring(table.concat(t)) == nil) +end + diff --git a/test/misc/coro_traceback.lua b/test/misc/coro_traceback.lua new file mode 100644 index 0000000000..2676d2c68e --- /dev/null +++ b/test/misc/coro_traceback.lua @@ -0,0 +1,8 @@ + +local co = coroutine.create(function() + local x = nil + local y = x.x +end) +assert(coroutine.resume(co) == false) +debug.traceback(co) + diff --git a/test/misc/coro_yield.lua b/test/misc/coro_yield.lua new file mode 100644 index 0000000000..ae3206e05b --- /dev/null +++ b/test/misc/coro_yield.lua @@ -0,0 +1,111 @@ +local create = coroutine.create +local wrap = coroutine.wrap +local resume = coroutine.resume +local yield = coroutine.yield + +-- Test stack overflow handling on return from coroutine. +do + wrap(function() + local co = create(function() + yield(string.byte(string.rep(" ", 100), 1, 100)) + end) + assert(select('#', resume(co)) == 101) + end)() +end + +do + wrap(function() + local f = wrap(function() + yield(string.byte(string.rep(" ", 100), 1, 100)) + end) + assert(select('#', f()) == 100) + end)() +end + +do + local function cogen(x) + return wrap(function(n) repeat x = x+n; n = yield(x) until false end), + wrap(function(n) repeat x = x*n; n = yield(x) until false end) + end + + local a,b=cogen(3) + local c,d=cogen(5) + assert(d(b(c(a(d(b(c(a(1)))))))) == 168428160) +end + +do + local function verify(what, expect, ...) + local got = {...} + for i=1,100 do + if expect[i] ~= got[i] then + error("FAIL " .. what) + end + if expect[i] == nil then + break + end + end + end + + local function cofunc(...) + verify("call", { 1, "foo" }, ...) + verify("yield", { "bar" }, yield(2, "test")) + verify("pcall yield", { true, "again" }, pcall(yield, "from pcall")) + return "end" + end + + local co = create(cofunc) + verify("resume", { true, 2, "test" }, resume(co, 1, "foo")) + verify("resume pcall", { true, "from pcall" }, resume(co, "bar")) + verify("resume end", { true, "end" }, resume(co, "again")) +end + +do + local function verify(expect, func, ...) + local co = create(func) + for i=1,100 do + local ok, res = resume(co, ...) + if not ok then + if expect[i] ~= nil then + error("too few results: ["..i.."] = "..tostring(expect[i]).." (got: "..tostring(res)..")") + end + break + end + if expect[i] ~= res then + error("bad result: ["..i.."] = "..tostring(res).." (should be: "..tostring(expect[i])..")") + end + end + end + + verify({ 42, 99 }, + function(x) pcall(yield, x) return 99 end, + 42) + + verify({ 42, 99 }, + function(x) pcall(function(y) yield(y) end, x) return 99 end, + 42) + + verify({ 42, 99 }, + function(x) xpcall(yield, debug.traceback, x) return 99 end, + 42) + + verify({ 45, 44, 43, 42, 99 }, + function(x, y) + for i in + function(o, k) + yield(o+k) + if k ~= 0 then return k-1 end + end,x,y do + end + return 99 + end, + 42, 3) + + verify({ 84, 99 }, + function(x) + local o = setmetatable({ x }, + {__add = function(a, b) yield(a[1]+b[1]) return 99 end }) + return o+o + end, + 42) +end + diff --git a/test/misc/debug_gc.lua b/test/misc/debug_gc.lua new file mode 100644 index 0000000000..30fb2b99b9 --- /dev/null +++ b/test/misc/debug_gc.lua @@ -0,0 +1,47 @@ + +-- Do not run this test unless the JIT compiler is turned off. +if jit and jit.status and jit.status() then return end + +local caught, caught_line, caught_mm + +local function gcmeta() + if caught ~= "end" then +-- print(debug.traceback()) + -- This may point to the wrong instruction if in a JIT trace. + -- But there's no guarantee if, when or where any GC steps occur. + local dbg = debug.getinfo(2) + caught_line = dbg.currentline + caught_mm = debug.getinfo(1).name + caught = true + end +end + +local function testgc(mm, f) + collectgarbage() + caught = false + local u = newproxy(true) + getmetatable(u).__gc = gcmeta + u = nil + for i=1,100000 do + f(i) + -- This check may be hoisted. __gc is not supposed to have side-effects. + if caught then break end + end + if not caught then + error(mm.." metamethod not called", 2) + end + if type(caught_line) ~= "number" or caught_line < 0 then + error("bad linenumber in debug info", 2) + end + if caught_mm ~= mm then + error("bad name for metamethod in debug info", 2) + end +end + +local x +testgc("__gc", function(i) x = {} end) +testgc("__gc", function(i) x = {1} end) +testgc("__gc", function(i) x = function() end end) +testgc("__concat", function(i) x = i.."" end) + +caught = "end" diff --git a/test/misc/debug_meta.lua b/test/misc/debug_meta.lua new file mode 100644 index 0000000000..5954bbfad5 --- /dev/null +++ b/test/misc/debug_meta.lua @@ -0,0 +1,79 @@ + +local what + +local function mm(a, b) + local dbg = debug.getinfo(1) + what = dbg.namewhat == "metamethod" and dbg.name or + dbg.namewhat.." "..(dbg.name or "?") +end + +local function ck(s) + assert(what == s, "bad debug info for metamethod "..s) +end + +local mt = { + __index = mm, + __newindex = mm, + __eq = mm, + __add = mm, + __sub = mm, + __mul = mm, + __div = mm, + __mod = mm, + __pow = mm, + __unm = mm, + __len = mm, + __lt = mm, + __le = mm, + __concat = mm, + __call = mm, +} + +local t = setmetatable({}, mt) +local t2 = setmetatable({}, mt) + +local x = t.x; ck("__index") +t.x = 1; ck("__newindex") +local x = t + t; ck("__add") +local x = t - t; ck("__sub") +local x = t * t; ck("__mul") +local x = t / t; ck("__div") +local x = t % t; ck("__mod") +local x = t ^ t; ck("__pow") +local x = -t; ck("__unm") +--local x = #t; ck("__len") -- Not called for tables +local x = t..t; ck("__concat") +local x = t(); ck("local t") + +local x = t == t2; ck("__eq") +local x = t ~= t2; ck("__eq") +local x = t < t2; ck("__lt") +local x = t > t2; ck("__lt") +local x = t <= t2; ck("__le") +local x = t >= t2; ck("__le") + +local u = newproxy() +local u2 = newproxy() +debug.setmetatable(u, mt) +debug.setmetatable(u2, mt) + +local x = u.x; ck("__index") +u.x = 1; ck("__newindex") +local x = u + u; ck("__add") +local x = u - u; ck("__sub") +local x = u * u; ck("__mul") +local x = u / u; ck("__div") +local x = u % u; ck("__mod") +local x = u ^ u; ck("__pow") +local x = -u; ck("__unm") +local x = #u; ck("__len") +local x = u..u; ck("__concat") +local x = u(); ck("local u") + +local x = u == u2; ck("__eq") +local x = u ~= u2; ck("__eq") +local x = u < u2; ck("__lt") +local x = u > u2; ck("__lt") +local x = u <= u2; ck("__le") +local x = u >= u2; ck("__le") + diff --git a/test/misc/dse_array.lua b/test/misc/dse_array.lua new file mode 100644 index 0000000000..af1725fe20 --- /dev/null +++ b/test/misc/dse_array.lua @@ -0,0 +1,200 @@ + +local assert = assert + +-- Same value ---------------------------------------------------------------- + +-- 1. Store with same ref and same value. +-- 2nd store eliminated. All stores in loop eliminated. +do + local t = { 1, 2 } + for i=1,100 do + t[1] = 11 + assert(t[1] == 11) + t[1] = 11 + assert(t[1] == 11) + end + assert(t[1] == 11) +end + +-- 2. Store with different tab, same idx and same value. +-- All stores in loop eliminated. +do + local t1 = { 1, 2 } + local t2 = { 1, 2 } + for i=1,100 do + t1[1] = 11 + assert(t1[1] == 11) + t2[1] = 11 + assert(t2[1] == 11) + end + assert(t1[1] == 11) + assert(t2[1] == 11) +end + +-- 3. Store with same tab, different const idx and same value. +-- All stores in loop eliminated. Also disambiguated. +do + local t = { 1, 2 } + for i=1,100 do + t[1] = 11 + assert(t[1] == 11) + t[2] = 11 + assert(t[2] == 11) + end + assert(t[1] == 11) + assert(t[2] == 11) +end + +-- 4. Store with different tab, different const idx and same value. +-- All stores in loop eliminated. Also disambiguated. +do + local t1 = { 1, 2 } + local t2 = { 1, 2 } + for i=1,100 do + t1[1] = 11 + assert(t1[1] == 11) + t2[2] = 11 + assert(t2[2] == 11) + end + assert(t1[1] == 11) + assert(t2[2] == 11) +end + +-- 5. Store with different tab, different non-const idx and same value. +-- All stores in loop eliminated. Not disambiguated (but not needed). +do + local t1 = { 1, 2 } + local t2 = { 1, 2 } + local k = 1 + for i=1,100 do + t1[k] = 11 + assert(t1[k] == 11) + t2[2] = 11 + assert(t2[2] == 11) + end + assert(t1[1] == 11) + assert(t2[2] == 11) +end + +-- 6. Store with same ref, same value and aliased loads. +-- 2nd store eliminated. Not disambiguated (but not needed). +do + local t1 = { 1, 2 } + local t2 = t1 + for i=1,100 do + t1[1] = 11 + assert(t2[1] == 11) + t1[1] = 11 + assert(t2[1] == 11) + end + assert(t1[1] == 11) +end + +-- Different value ----------------------------------------------------------- + +-- 7. Store with same ref and different value. +-- 1st store eliminated. All stores in loop eliminated. +do + local t = { 1, 2 } + for i=1,100 do + assert(true) + t[1] = 11 + assert(t[1] == 11) + t[1] = 22 + assert(t[1] == 22) + end + assert(t[1] == 22) +end + +-- 8. Store with different tab, same idx and different value. +-- Cannot eliminate any stores (would need dynamic disambiguation). +do + local t1 = { 1, 2 } + local t2 = { 1, 2 } + for i=1,100 do + assert(true) + t1[1] = 11 + assert(t1[1] == 11) + t2[1] = 22 + assert(t2[1] == 22) + end + assert(t1[1] == 11) + assert(t2[1] == 22) +end + +-- 9. Store with same tab, different const idx and different value. +-- Disambiguated. All stores in loop eliminated. +do + local t = { 1, 2 } + for i=1,100 do + assert(true) + t[1] = 11 + assert(t[1] == 11) + t[2] = 22 + assert(t[2] == 22) + end + assert(t[1] == 11) + assert(t[2] == 22) +end + +-- 10. Store with different tab, different const idx and different value. +-- Disambiguated. All stores in loop eliminated. +do + local t1 = { 1, 2 } + local t2 = { 1, 2 } + for i=1,100 do + assert(true) + t1[1] = 11 + assert(t1[1] == 11) + t2[2] = 22 + assert(t2[2] == 22) + end + assert(t1[1] == 11) + assert(t2[2] == 22) +end + +-- 11. Store with different tab, different non-const idx and different value. +-- Cannot eliminate any stores (would need dynamic disambiguation). +do + local t1 = { 1, 2 } + local t2 = { 1, 2 } + local k = 1 + for i=1,100 do + assert(true) + t1[k] = 11 + assert(t1[k] == 11) + t2[2] = 22 + assert(t2[2] == 22) + end + assert(t1[1] == 11) + assert(t2[2] == 22) +end + +-- 12. Store with same ref, different value and aliased loads. +-- Cannot eliminate any stores (would need dynamic disambiguation). +do + local t1 = { 1, 2 } + local t2 = t1 + for i=1,100 do + assert(true) + t1[1] = 11 + assert(t2[1] == 11) + t1[1] = 22 + assert(t2[1] == 22) + end + assert(t1[1] == 22) +end + +-- CALLL must inhibit DSE. +do + local a,b + local t = {1,2} + for i=1,100 do + t[2]=nil + a=#t + t[2]=2 + b=#t + end + assert(a == 1 and b == 2) +end + diff --git a/test/misc/dse_field.lua b/test/misc/dse_field.lua new file mode 100644 index 0000000000..497cd9ecfe --- /dev/null +++ b/test/misc/dse_field.lua @@ -0,0 +1,77 @@ + +local getmetatable, setmetatable = getmetatable, setmetatable + +-- 1. Store with same ref and same value. All stores in loop eliminated. +do + local mt = {} + local t = {} + for i=1,100 do + setmetatable(t, mt) + assert(getmetatable(t) == mt) + setmetatable(t, mt) + assert(getmetatable(t) == mt) + end + assert(getmetatable(t) == mt) +end + +-- 2. Store with different ref and same value. All stores in loop eliminated. +do + local mt = {} + local t1 = {} + local t2 = {} + for i=1,100 do + setmetatable(t1, mt) + assert(getmetatable(t1) == mt) + setmetatable(t2, mt) + assert(getmetatable(t2) == mt) + end + assert(getmetatable(t1) == mt) + assert(getmetatable(t2) == mt) +end + +-- 3. Store with different ref and different value. Cannot eliminate any stores. +do + local mt1 = {} + local mt2 = {} + local t1 = {} + local t2 = {} + for i=1,100 do + setmetatable(t1, mt1) + assert(getmetatable(t1) == mt1) + setmetatable(t2, mt2) + assert(getmetatable(t2) == mt2) + end + assert(getmetatable(t1) == mt1) + assert(getmetatable(t2) == mt2) +end + +-- 4. Store with same ref and different value. 2nd store remains in loop. +do + local mt1 = {} + local mt2 = {} + local t = {} + for i=1,100 do + setmetatable(t, mt1) + assert(getmetatable(t) == mt1) + setmetatable(t, mt2) + assert(getmetatable(t) == mt2) + end + assert(getmetatable(t) == mt2) +end + +-- 5. Store with same ref, different value and aliased loads. +-- Cannot eliminate any stores. +do + local mt1 = {} + local mt2 = {} + local t1 = {} + local t2 = t1 + for i=1,100 do + setmetatable(t1, mt1) + assert(getmetatable(t2) == mt1) + setmetatable(t1, mt2) + assert(getmetatable(t2) == mt2) + end + assert(getmetatable(t1) == mt2) +end + diff --git a/test/misc/dualnum.lua b/test/misc/dualnum.lua new file mode 100644 index 0000000000..5f1288c8df --- /dev/null +++ b/test/misc/dualnum.lua @@ -0,0 +1,47 @@ + +-- Positive overflow +do + local x = 0 + for i=2147483446,2147483647,2 do x = x + 1 end + assert(x == 101) +end + +-- Negative overflow +do + local x = 0 + for i=-2147483447,-2147483648,-2 do x = x + 1 end + assert(x == 101) +end + +-- SLOAD with number to integer conversion. +do + local k = 1 + local a, b, c = 1/k, 20/k, 1/k + for i=1,20 do + for j=a,b,c do end + end +end + +do + local function fmin(a, b) + for i=1,100 do a = math.min(a, b) end + return a + end + local function fmax(a, b) + for i=1,100 do a = math.max(a, b) end + return a + end + assert(fmin(1, 3) == 1) + assert(fmin(3, 1) == 1) + assert(fmin(-1, 3) == -1) + assert(fmin(3, -1) == -1) + assert(fmin(-1, -3) == -3) + assert(fmin(-3, -1) == -3) + assert(fmax(1, 3) == 3) + assert(fmax(3, 1) == 3) + assert(fmax(-1, 3) == 3) + assert(fmax(3, -1) == 3) + assert(fmax(-1, -3) == -1) + assert(fmax(-3, -1) == -1) +end + diff --git a/test/misc/exit_frame.lua b/test/misc/exit_frame.lua new file mode 100644 index 0000000000..897984872b --- /dev/null +++ b/test/misc/exit_frame.lua @@ -0,0 +1,80 @@ +do + g = 0 + gf = 1 + gz = 2 + + local function f(i) + if i == 90 then + gf = gf + 1 + return true + end + g = g + 1 + end + + local function z(i) + if f(i) then + gz = gz + 1 + end + end + + for j=1,5 do + for i=1,100 do z(i) end + end + + assert(g == 495) + assert(gf == 6) + assert(gz == 7) +end + +do + local f, g + function f(j) + if j >= 0 then return g(j-1) end + end + function g(j) + for i=1,200 do + if i > 100 then return f(j) end + end + end + for k=1,20 do g(20) end +end + +do + local f, g + function f(j, k) + if j >= 0 then return g(j-1, k) end + if k >= 0 then return g(20, k-1) end + end + function g(j, k) + for i=1,200 do + if i > 100 then return f(j, k) end + end + end + g(20, 20) +end + +do + local k = 0 + local f, g + + function g(a) + -- 'a' is an SLOAD #1 from f's frame and still at slot #1 + -- Avoid losing a in exit if the SLOAD is ignored + if k > 10 then k = 0 end + k= k + 1 + return f(a) + end + + function f(a,b,c,d,e) + if not e then e =1 end + a=a+1 + if a > 1000 then return end + for i=1,100 do + e=e+1 + if i > 90 then return g(a) end + end + end + + f(1,2,3,4,5) +end + diff --git a/test/misc/exit_growstack.lua b/test/misc/exit_growstack.lua new file mode 100644 index 0000000000..548b55565a --- /dev/null +++ b/test/misc/exit_growstack.lua @@ -0,0 +1,24 @@ +local function f(i) + local a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a; + local a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a; + local a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a; + if i==90 then return end -- Exit needs to grow stack before slot fill. +end +for j=1,5 do + collectgarbage() -- Shrink stack. + for i=1,100 do f(i) end +end + +local function g(i) + if i==90 then return end -- Exit needs to grow stack after slot fill. + do return end + do + local a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a; + local a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a; + local a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a; + end +end +for j=1,5 do + collectgarbage() -- Shrink stack. + for i=1,100 do g(i) end +end diff --git a/test/misc/exit_jfuncf.lua b/test/misc/exit_jfuncf.lua new file mode 100644 index 0000000000..7e452ef44f --- /dev/null +++ b/test/misc/exit_jfuncf.lua @@ -0,0 +1,30 @@ + +local assert = assert + +local function rec(a, b, c, d, e, f) + assert(f == a+1) + if b == 0 then return 7 end + do local x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35, x36, x37, x38, x39, x40, x41, x42, x43, x44, x45, x46, x47, x48, x49, x50, x51, x52, x53, x54, x55, x56, x57, x58, x59, x60, x61, x62, x63, x64, x65, x66, x67, x68, x69, x70, x71, x72, x73, x74, x75, x76, x77, x78, x79, x80, x81, x82, x83, x84, x85, x86, x87, x88, x89, x90, x91, x92, x93, x94, x95, x96, x97, x98, x99, x100 end + return rec(a, b-1, c, d, e, f)+1 +end + +-- Compile recursive function. +assert(rec(42, 200, 1, 2, 3, 43) == 207) + +local function trec() + return rec(42, 0, 1, 2, 3, 43) +end + +-- Compile function jumping to JFUNCF. +for i=1,200 do + gcinfo() + assert(trec() == 7) +end + +-- Shrink stack. +for j=1,10 do collectgarbage() end + +-- Cause an exit due to stack growth with PC pointing to JFUNCF. +-- Needs to load RD with nres+1 and not with the bytecode RD. +assert(trec() == 7) + diff --git a/test/misc/fac.lua b/test/misc/fac.lua new file mode 100644 index 0000000000..86dc427070 --- /dev/null +++ b/test/misc/fac.lua @@ -0,0 +1,13 @@ +local function fac(n) + local x = 1 + for i=2,n do + x = x * i + end + return x +end + +if arg and arg[1] then + print(fac(tonumber(arg[1]))) +else + assert(fac(10) == 3628800) +end diff --git a/test/misc/fastfib.lua b/test/misc/fastfib.lua new file mode 100644 index 0000000000..80394bdd08 --- /dev/null +++ b/test/misc/fastfib.lua @@ -0,0 +1,26 @@ + +local function ffib(n) + if n <= 2 then return n,1 end + if n % 2 == 1 then + local a,b = ffib((n-1)/2) + local aa = a*a + return aa+a*(b+b), aa+b*b + else + local a,b = ffib(n/2-1) + local ab = a+b + return ab*ab+a*a, (ab+b)*a + end +end + +local function fib(n) + return (ffib(n)) +end + +if arg and arg[1] then + local n = tonumber(arg and arg[1]) or 10 + io.write(string.format("Fib(%d): %.0f\n", n, fib(n))) +else + assert(fib(40) == 165580141) + assert(fib(39) == 102334155) + assert(fib(77) == 8944394323791464) +end diff --git a/test/misc/fib.lua b/test/misc/fib.lua new file mode 100644 index 0000000000..74eb046496 --- /dev/null +++ b/test/misc/fib.lua @@ -0,0 +1,11 @@ +local function fib(n) + if n < 2 then return 1 end + return fib(n-2) + fib(n-1) +end + +if arg and arg[1] then + local n = tonumber(arg[1]) or 10 + io.write(string.format("Fib(%d): %d\n", n, fib(n))) +else + assert(fib(27) == 317811) +end diff --git a/test/misc/for_dir.lua b/test/misc/for_dir.lua new file mode 100644 index 0000000000..4dd38dee0d --- /dev/null +++ b/test/misc/for_dir.lua @@ -0,0 +1,13 @@ + +local a,b,c = 10,1,-1 +for i=1,20 do + if c == -1 then + a,b,c = 1,10,1 + else + a,b,c = 10,1,-1 + end + local x = 0 + for i=a,b,c do for j=1,10 do end x=x+1 end + assert(x == 10) +end + diff --git a/test/misc/fori_coerce.lua b/test/misc/fori_coerce.lua new file mode 100644 index 0000000000..7330943bd8 --- /dev/null +++ b/test/misc/fori_coerce.lua @@ -0,0 +1,33 @@ + +do + local n = 1 + local x = 0 + for i=1,20 do + for j=n,100 do x = x + 1 end + if i == 13 then n = "2" end + end + assert(x == 1993) +end + +do + local n = 1 + local x = 0 + for i=1,20 do + for j=n,100 do x = x + 1 end + if i == 10 then n = "2" end + end + assert(x == 1990) +end + +do + local function f() + local n = 1 + local x = 0 + for i=1,20 do + for j=n,100 do x = x + 1 end + if i == 10 then n = "x" end + end + end + assert(not pcall(f)) +end + diff --git a/test/misc/fuse.lua b/test/misc/fuse.lua new file mode 100644 index 0000000000..c4e077c87d --- /dev/null +++ b/test/misc/fuse.lua @@ -0,0 +1,8 @@ + +-- Don't fuse i+101 on x64. +-- Except if i is sign-extended to 64 bit or addressing is limited to 32 bit. +do + local t = {} + for i=-100,-1 do t[i+101] = 1 end +end + diff --git a/test/misc/fwd_hrefk_rollback.lua b/test/misc/fwd_hrefk_rollback.lua new file mode 100644 index 0000000000..1c4d597923 --- /dev/null +++ b/test/misc/fwd_hrefk_rollback.lua @@ -0,0 +1,34 @@ + +-- https://github.com/LuaJIT/LuaJIT/issues/124 + +local function foo(a, b, f) + return f and (a.f0 < b.f1 and + b.f0 < a.f1 and + a.f2 < b.f3 and + b.f2 < a.f3) +end + +local function bar(f0, f1, f2, f3, X, f) + for _, v in ipairs(X) do + local b = {} + b.f0 = 0 + b.f2 = v + b.f1 = b.f0 + 1 + b.f3 = b.f2 + 1 + + if foo({f0 = f0, f1 = f1, f2 = f2, f3 = f3}, b, f) then + return false + end + end + + return true +end + +local X = { 0, 1, 0, 0 } + +for i = 1, 20 do + assert(bar(0, 1, 2, 3, X, true)) +end + +assert(not bar(0, 1, 1, 2, X, true)) + diff --git a/test/misc/fwd_tnew_tdup.lua b/test/misc/fwd_tnew_tdup.lua new file mode 100644 index 0000000000..105263ca08 --- /dev/null +++ b/test/misc/fwd_tnew_tdup.lua @@ -0,0 +1,78 @@ + +-- 1. +do + local x = 2 + for i=1,100 do + local t = {} -- TNEW: DCE + x = t.foo -- HREF -> niltv: folded + end + assert(x == nil) +end + +-- 2. +do + local x = 2 + for i=1,100 do + local t = {1} -- TDUP: DCE + x = t.foo -- HREF -> niltv: folded + end + assert(x == nil) +end + +-- 3. +do + local x = 2 + for i=1,100 do + local t = {} + t[1] = 11 -- NEWREF + HSTORE + x = t[1] -- AREF + ALOAD, no forwarding, no fold + end + assert(x == 11) +end + +-- 4. HREFK not eliminated. Ditto for the EQ(FLOAD(t, #tab.hmask), k). +do + local x = 2 + for i=1,100 do + local t = {} + t.foo = 11 -- NEWREF + HSTORE + x = t.foo -- HREFK + HLOAD: store forwarding + end + assert(x == 11) +end + +-- 5. HREFK not eliminated. Ditto for the EQ(FLOAD(t, #tab.hmask), k). +do + local x = 2 + for i=1,100 do + local t = {foo=11} -- TDUP + x = t.foo -- HREFK + non-nil HLOAD: folded + end + assert(x == 11) +end + +-- 6. +do + local x = 2 + local k = 1 + for i=1,100 do + local t = {[0]=11} -- TDUP + t[k] = 22 -- AREF + ASTORE aliasing + x = t[0] -- AREF + ALOAD, no fold + end + assert(x == 11) +end + +-- 7. +do + local setmetatable = setmetatable + local mt = { __newindex = function(t, k, v) + assert(k == "foo") + assert(v == 11) + end } + for i=1,100 do + local t = setmetatable({}, mt) + t.foo = 11 + end +end + diff --git a/test/misc/fwd_upval.lua b/test/misc/fwd_upval.lua new file mode 100644 index 0000000000..7fb360d2d3 --- /dev/null +++ b/test/misc/fwd_upval.lua @@ -0,0 +1,57 @@ + +-- 1. Open upvalue above base slot, aliasing an SSA value. +do + local x = 7 + local function a() x = x + 1 end + local function b() x = x + 2 end + for i=1,100 do a(); b(); x = x + 5 end + assert(x == 807) +end + +-- 2. Open upvalue below base slot. UREFO CSE for a.x + b.x, but not x in loop. +-- ULOAD not disambiguated. 2x ULOAD + 2x USTORE (+ 1x DSE USTORE). +do + local x = 7 + (function() + local function a() x = x + 1 end + local function b() x = x + 2 end + for i=1,100 do a(); b(); x = x + 5 end + end)() + assert(x == 807) +end + +-- 3. Closed upvalue. UREFC CSE for a.x + b.x, but not x in loop. +-- ULOAD not disambiguated. 2x ULOAD + 2x USTORE (+ 1x DSE for USTORE). +do + local xx = (function() + local x = 7 + local function a() x = x + 1 end + local function b() x = x + 2 end + return function() for i=1,100 do a(); b(); x = x + 5 end; return x end + end)()() + assert(xx == 807) +end + +-- 4. Open upvalue below base slot. Forwarded. 1x USTORE (+ 1x DSE USTORE). +do + local x = 7 + (function() + local function a() x = x + 1 end + for i=1,100 do a(); a() end + end)() + assert(x == 207) +end + +-- 5. Closed upvalue. Forwarded. 1x USTORE (+ 1x DSE USTORE). +do + local xx = (function() + local x = 7 + return function() + local function a() x = x + 1 end + for i=1,100 do a(); a() end + return x + end + end)()() + assert(xx == 207) +end + diff --git a/test/misc/gc_rechain.lua b/test/misc/gc_rechain.lua new file mode 100644 index 0000000000..285f408671 --- /dev/null +++ b/test/misc/gc_rechain.lua @@ -0,0 +1,32 @@ + +do + local k + + collectgarbage() + + local t = {} + t.ac = 1 + + t.nn = 1 + t.mm = 1 + t.nn = nil + t.mm = nil + + k = "a".."i" + t[k] = 2 + + t.ad = 3 + + t[k] = nil + k = nil + + collectgarbage() + + k = "a".."f" + t[k] = 4 + + t.ak = 5 + + assert(t[k] == 4) +end + diff --git a/test/misc/gc_trace.lua b/test/misc/gc_trace.lua new file mode 100644 index 0000000000..bc38ce0ca3 --- /dev/null +++ b/test/misc/gc_trace.lua @@ -0,0 +1,37 @@ + +if not jit or not jit.status or not jit.status() then return end + +collectgarbage() +for j=1,100 do + loadstring("for i=1,100 do end")() +end +local jutil = require("jit.util") +assert(jutil.traceinfo(90) == nil) +collectgarbage() +assert(jutil.traceinfo(1) == nil) +assert(jutil.traceinfo(2) == nil) +assert(jutil.traceinfo(3) == nil) + +do + local f + local function reccb(tr) + if f == nil then + collectgarbage() + local info = jutil.traceinfo(tr) + jutil.tracek(tr, -info.nk) + -- Error in lj_ir_kvalue() if KGC not marked. + -- Only caught with assertions or Valgrind. + end + end + jit.attach(reccb, "record") + for i=1,200 do + if i % 5 == 0 then + f = function() end + elseif f then + f() + f = nil + end + end + jit.attach(reccb) +end + diff --git a/test/misc/gcstep.lua b/test/misc/gcstep.lua new file mode 100644 index 0000000000..533356b76e --- /dev/null +++ b/test/misc/gcstep.lua @@ -0,0 +1,33 @@ + +local function testgc(what, func) + collectgarbage() + local oc = gcinfo() + func() + local nc = gcinfo() + assert(nc < oc*4, "GC step missing for "..what) +end + +testgc("TNEW", function() + for i=1,10000 do + local t = {} + end +end) + +testgc("TDUP", function() + for i=1,10000 do + local t = {1} + end +end) + +testgc("FNEW", function() + for i=1,10000 do + local function f() end + end +end) + +testgc("CAT", function() + for i=1,10000 do + local s = "x"..i + end +end) + diff --git a/test/misc/getfenv.lua b/test/misc/getfenv.lua new file mode 100644 index 0000000000..48a261a7a0 --- /dev/null +++ b/test/misc/getfenv.lua @@ -0,0 +1,15 @@ + +do + local x + local function f() + x = getfenv(0) + end + local co = coroutine.create(f) + local t = {} + debug.setfenv(co, t) + for i=1,50 do f() f() f() end + assert(x == getfenv(0)) + coroutine.resume(co) + assert(x == t) +end + diff --git a/test/misc/goto.lua b/test/misc/goto.lua new file mode 100644 index 0000000000..8553a8aa90 --- /dev/null +++ b/test/misc/goto.lua @@ -0,0 +1,156 @@ + +-- Basic goto and label semantics. +do + local function expect(src, msg) + local ok, err = loadstring(src) + if msg then + assert(not ok and string.find(err, msg)) + else + assert(ok) + end + end + + -- Error: duplicate label. + expect("::a:: ::a::", "'a'") + expect("::a:: ::b:: do ::b:: end ::a::", "'a'") + + -- Error: undefined label. + expect("goto a", "'a'") + expect("goto a; ::b::", "'a'") + expect("do ::a:: end; goto a", "'a'") + expect("goto a; do ::a:: end", "'a'") + expect("break", "break") + expect("if x then break end", "break") + + -- Error: goto into variable scope. + expect("goto a; local x; ::a:: local y", "'x'") + expect("do local v,w; goto a; end; local x; ::a:: local y", "'x'") + expect("repeat goto a; local x; ::a:: until x", "'x'") + + if os.getenv("LUA52") then + expect("goto = 1", "") + else + expect("goto = 1") + end + + ::a:: do goto a; ::a:: end -- Forward jump, not an infinite loop. +end + +-- Trailing label is considered to be out of scope. +do + local x = 11 + do + goto a + goto a + local y = 22 + x = y + ::a:: + ::b:: + end + assert(x == 11) + if os.getenv("LUA52") then + assert(loadstring([[ + local x = 11 + do + goto a + goto a + local y = 22 + x = y + ::a:: ;; + ::b:: ;; + end + return x + ]])() == 11) + end +end + +-- Simple loop with cross-jumping. +do + local x = 1 + while true do + goto b + ::a:: if x < 100 then goto c end + goto d + ::b:: x = x + 1; goto a + ::c:: + end + ::d:: + assert(x == 100) +end + +-- Backwards goto must close upval. +do + local t = {} + local i = 1 + ::a:: + local x + t[i] = function() return x end + x = i + i = i + 1 + if i <= 2 then goto a end + assert(t[1]() == 1) + assert(t[2]() == 2) +end + +-- Break must close upval, even if closure is parsed after break. +do + local foo + repeat + local x + ::a:: + if x then break end + function foo() return x end + x = true + goto a + until false + assert(foo() == true) +end + +-- Label prevents joining to KNIL. +do + local k = 0 + local x + ::foo:: + local y + assert(y == nil) + y = true + k = k + 1 + if k < 2 then goto foo end +end + +-- Break resolved from the right scope. +do + local function p(lvl) + lvl = lvl or 1 + while true do + lvl = lvl + 1 + if lvl == nil then break end + local idx = 1 + while true do + if key == nil then break end + idx = idx + 1 + end + end + end +end + +-- Do not join twice with UCLO. +do + while true do + do + local x + local function f() return x end + end + break + end + + while true do + do + local x + local function f() return x end + end + goto foo + end + ::foo:: +end + diff --git a/test/misc/hook_active.lua b/test/misc/hook_active.lua new file mode 100644 index 0000000000..37dfc37937 --- /dev/null +++ b/test/misc/hook_active.lua @@ -0,0 +1,95 @@ +local ctest = require("ctest") + +local called = 0 +local function clearhook() debug.sethook(nil, "", 0) end + +-- Return from pcall with active hook must prepend true. FF pcall. +called = 0 +debug.sethook(function() called=called+1; assert(pcall(function() end) == true); clearhook() end, "", 1) +do local x = 1 end +assert(called == 1) + +-- Hook with special caught error must not unblock hooks. FF pcall. +called = 0 +debug.sethook(function() called=called+1; pcall(nil); clearhook() end, "", 1) +do local x = 1 end +assert(called == 1) + +-- Hook with caught error must not unblock hooks. FF pcall. +called = 0 +local function p2() error("") end +debug.sethook(function() called=called+1; pcall(p2); clearhook() end, "", 1) +do local x = 1 end +assert(called == 1) + +-- Hook with special caught error must not unblock hooks. C pcall. +called = 0 +debug.sethook(function() called=called+1; ctest.pcall(nil); clearhook() end, "", 1) +do local x = 1 end +assert(called == 1) + +-- Hook with caught error must not unblock hooks. C pcall +called = 0 +local function p2() error("") end +debug.sethook(function() called=called+1; ctest.pcall(p2); clearhook() end, "", 1) +do local x = 1 end +assert(called == 1) + +-- Regular pcall must not block hooks. +debug.sethook(function() called=called+1 end, "", 1) +pcall(function() end) +called = 0 +do local x = 1 end +assert(called > 0) +pcall(function() error("") end) +called = 0 +do local x = 1 end +assert(called > 0) +ctest.pcall(function() end) +called = 0 +do local x = 1 end +assert(called > 0) +ctest.pcall(function() error("") end) +called = 0 +do local x = 1 end +assert(called > 0) +clearhook() + +-- Hook with uncaught error must unblock hooks. FF pcall +called = 0 +pcall(function() + debug.sethook(function() + local old = called + called = 1 + if old == 0 then error("") end + end, "", 1) + do local x = 1 end +end) +assert(called == 1) +called = 2 +do local x = 1 end +assert(called == 1, "hook not unblocked after uncaught error") +clearhook() +called = 2 +do local x = 1 end +assert(called == 2) + +-- Hook with uncaught error must unblock hooks. C pcall +called = 0 +ctest.pcall(function() + debug.sethook(function() + local old = called + called = 1 + if old == 0 then error("") end + end, "", 1) + do local x = 1 end +end) +assert(called == 1) +called = 2 +do local x = 1 end +assert(called == 1, "hook not unblocked after uncaught error") +clearhook() +called = 2 +do local x = 1 end +assert(called == 2) + diff --git a/test/misc/hook_line.lua b/test/misc/hook_line.lua new file mode 100644 index 0000000000..36f710807a --- /dev/null +++ b/test/misc/hook_line.lua @@ -0,0 +1,41 @@ +local lines = {} +local function hook() + lines[#lines+1] = debug.getinfo(2).currentline +end + +local function dummy() +end -- <-- line 7 + +debug.sethook(hook, "l", 0) +-- <-- line 10 +local x +dummy() +local y = 1 +dummy() dummy() +local z = 2; local r = true +while y < 4 do y = y + 1 end +while z < 4 do + z = z + 1 +end +-- <-- line 20 +local v +debug.sethook(nil, "", 0) + +assert(#lines > 0) +while lines[1] < 10 do table.remove(lines, 1) end +while lines[#lines] > 20 do table.remove(lines) end + +local s = table.concat(lines, " ") +assert(s == "11 12 7 13 14 7 7 15 16 16 16 16 17 18 17 18 17" or + s == "11 12 7 13 14 7 14 7 15 16 16 16 16 17 18 17 18 17") + +lines = {} +local function f() + if true then return end + local function x() end +end -- <-- line 36 +debug.sethook(hook, "l", 0) +f() +debug.sethook(nil, "", 0) +for i=1,#lines do assert(lines[i] ~= 36) end + diff --git a/test/misc/hook_norecord.lua b/test/misc/hook_norecord.lua new file mode 100644 index 0000000000..8e7cba05ea --- /dev/null +++ b/test/misc/hook_norecord.lua @@ -0,0 +1,12 @@ + +if not jit or not jit.status or not jit.status() then return end + +local called = false +local function f() local x = "wrong"; called = true end +jit.off(f) +debug.sethook(f, "", 5) +for i=1,1000 do local a,b,c,d,e,f=1,2,3,4,5,6 end +assert(called) +-- Check that no trace was generated. +assert(require("jit.util").traceinfo(1) == nil) + diff --git a/test/misc/hook_record.lua b/test/misc/hook_record.lua new file mode 100644 index 0000000000..6f1646dead --- /dev/null +++ b/test/misc/hook_record.lua @@ -0,0 +1,8 @@ + +if not jit or not jit.status or not jit.status() then return end + +debug.sethook(function() for i=1,100 do end end, "", 10) +for i=1,10 do end +debug.sethook() +assert((require("jit.util").traceinfo(1))) + diff --git a/test/misc/hook_top.lua b/test/misc/hook_top.lua new file mode 100644 index 0000000000..f809fcea64 --- /dev/null +++ b/test/misc/hook_top.lua @@ -0,0 +1,55 @@ + +local t = {} +for i=1,26 do t[i] = string.char(96+i) end + +local function tcheck(t1, t2) + assert(#t1 == #t2) + for i=1,#t1 do assert(t1[i] == t2[i]) end +end + +local function foo1(...) -- VARG RETM + return ... +end + +local function foo2(...) -- VARG UCLO RETM + local function dummy() end + return ... +end + +local function foo3(...) -- VARG UCLO -> RETM + do return ... end + local function dummy() end +end + +local function foo4() -- UCLO UCLO RET + do + local x + local function dummy() return x end + end +end + +called = false +debug.sethook(function() local x = "wrong"; called = true end, "", 1) +tcheck(t, {foo1(unpack(t))}) -- CALLM TSETM +assert(called) +called = false +tcheck(t, {foo2(unpack(t))}) +assert(called) +called = false +tcheck(t, {foo2(unpack(t))}) +assert(called) +called = false +foo4() +assert(called) + +debug.sethook(function() + local name, val = debug.getlocal(2, 1) + assert(name == "a" and val == nil) + debug.setlocal(2, 1, "bar") + debug.sethook(nil) +end, "c") +local function foo5(a) + assert(a == "bar") +end +foo5() + diff --git a/test/misc/iter.lua b/test/misc/iter.lua new file mode 100644 index 0000000000..4811812eef --- /dev/null +++ b/test/misc/iter.lua @@ -0,0 +1,85 @@ + +do + local n = 0 + for k,v in pairs(_G) do + assert(_G[k] == v) + n = n + 1 + end + assert(n >= 40) +end + +do + local t = { 4,5,6,7,8,9,10 } + local n = 0 + for i,v in ipairs(t) do + assert(v == i+3) + n = n + 1 + end + assert(n == 7) +end + +do + local function count(t) + local n = 0 + for i,v in pairs(t) do + n = n + 1 + end + return n; + end + assert(count({ 4,5,6,nil,8,nil,10}) == 5) + assert(count({ [0] = 3, 4,5,6,nil,8,nil,10}) == 6) + assert(count({ foo=1, bar=2, baz=3 }) == 3) + assert(count({ foo=1, bar=2, baz=3, boo=4 }) == 4) + assert(count({ 4,5,6,nil,8,nil,10, foo=1, bar=2, baz=3 }) == 8) + local t = { foo=1, bar=2, baz=3, boo=4 } + t.bar = nil; t.boo = nil + assert(count(t) == 2) +end + +do + local t = {} + for i=1,100 do t[i]=i end + local n = 0 + for i,v in ipairs(t) do + assert(i == v) + n = n + 1 + end + assert(n == 100) +end + +do + local ok, err = pcall(next, _G, 1) + assert(not ok) + local ok, err = pcall(function() next(_G, 1) end) + assert(not ok) +end + +do + local t = {} + local o = {{}, {}} + for i=1,100 do + local c = i.."" + t[i] = c + o[1][c] = i + o[2][c] = i + end + o[1]["90"] = nil + + for _, c in ipairs(t) do + for i = 1, 2 do + o[i][c] = o[i][c] or 1 + end + end +end + +do + local t = { foo = 9, bar = 10, 4, 5, 6 } + local r = {} + local function dummy() end + local function f(next) + for k,v in next,t,nil do r[#r+1] = k; if v == 5 then f(dummy) end end + end + f(next) + assert(#r == 5) +end + diff --git a/test/misc/jit_flush.lua b/test/misc/jit_flush.lua new file mode 100644 index 0000000000..ead1e4e991 --- /dev/null +++ b/test/misc/jit_flush.lua @@ -0,0 +1,50 @@ + +if not jit or not jit.status or not jit.status() then return end + +for i=1,100 do + if i==50 then jit.flush(2) end + for j=1,100 do end + for j=1,100 do end +end + +jit.flush() + +local function f() for i=1,100 do end end +for i=1,100 do local x = gcinfo(); f() end + +jit.flush() + +local function fib(n) + if n < 2 then return 1 end + return fib(n-2) + fib(n-1) +end + +fib(11) + +jit.flush() + +local names = {} +for i=1,100 do names[i] = i end + +function f() + for k,v in ipairs(names) do end +end + +f() + +for i=1,2 do + f() + f() + jit.flush() +end + +jit.flush() + +jit.flush(1) -- ignored +jit.flush(2) -- ignored +for i=1,1e7 do end -- causes trace #1 + +jit.flush(2) -- ignored +jit.flush(1) -- ok +jit.flush(1) -- crashes + diff --git a/test/misc/kfold.lua b/test/misc/kfold.lua new file mode 100644 index 0000000000..1a532f16d8 --- /dev/null +++ b/test/misc/kfold.lua @@ -0,0 +1,81 @@ + +do + local y = 0 + for i=1,100 do local a, b = 23, 11; y = a+b end; assert(y == 23+11) + for i=1,100 do local a, b = 23, 11; y = a-b end; assert(y == 23-11) + for i=1,100 do local a, b = 23, 11; y = a*b end; assert(y == 23*11) + for i=1,100 do local a, b = 23, 11; y = a/b end; assert(y == 23/11) + for i=1,100 do local a, b = 23, 11; y = a%b end; assert(y == 23%11) + for i=1,100 do local a, b = 23, 11; y = a^b end; assert(y == 23^11) + + for i=1,100 do local a, b = 23.5, 11.5; y = a+b end; assert(y == 23.5+11.5) + for i=1,100 do local a, b = 23.5, 11.5; y = a-b end; assert(y == 23.5-11.5) + for i=1,100 do local a, b = 23.5, 11.5; y = a*b end; assert(y == 23.5*11.5) + for i=1,100 do local a, b = 23.5, 11.5; y = a/b end; assert(y == 23.5/11.5) + for i=1,100 do local a, b = 23.5, 11.5; y = a%b end; assert(y == 23.5%11.5) + for i=1,100 do local a, b = 8, 11.5; y = a^b end; assert(y == 8^11.5) +end + +do + local y = 0 + for i=1,100 do local a=23; y = math.abs(a) end; assert(y == math.abs(23)) + for i=1,100 do local a=-23; y = math.abs(a) end; assert(y == math.abs(-23)) + for i=1,100 do local a=23.5; y = math.abs(a) end; assert(y == math.abs(23.5)) + for i=1,100 do local a=-23.5; y = math.abs(a) end; assert(y==math.abs(-23.5)) + for i=1,100 do local a=-2^31; y = math.abs(a) end; assert(y==math.abs(-2^31)) +end + +do + local y = 0 + for i=1,100 do local a, b = 23, 11; y = math.atan2(a, b) end + assert(y == math.atan2(23, 11)) + for i=1,100 do local a, b = 23, 11; y = math.ldexp(a, b) end + assert(y == math.ldexp(23, 11)) +end + +do + local y = 0 + for i=1,100 do local a, b = 23, 11; y = math.min(a, b) end + assert(y == math.min(23, 11)) + for i=1,100 do local a, b = 23, 11; y = math.max(a, b) end + assert(y == math.max(23, 11)) + for i=1,100 do local a, b = 23.5, 11.5; y = math.min(a, b) end + assert(y == math.min(23.5, 11.5)) + for i=1,100 do local a, b = 23.5, 11.5; y = math.max(a, b) end + assert(y == math.max(23.5, 11.5)) + for i=1,100 do local a, b = 11, 23; y = math.min(a, b) end + assert(y == math.min(11, 23)) + for i=1,100 do local a, b = 11, 23; y = math.max(a, b) end + assert(y == math.max(11, 23)) + for i=1,100 do local a, b = 11.5, 23.5; y = math.min(a, b) end + assert(y == math.min(11.5, 23.5)) + for i=1,100 do local a, b = 11.5, 23.5; y = math.max(a, b) end + assert(y == math.max(11.5, 23.5)) +end + +do + local y = 0 + for i=1,100 do local a=23; y=math.floor(a) end assert(y==math.floor(23)) + for i=1,100 do local a=23.5; y=math.floor(a) end assert(y==math.floor(23.5)) + for i=1,100 do local a=-23; y=math.floor(a) end assert(y==math.floor(-23)) + for i=1,100 do local a=-23.5; y=math.floor(a) end assert(y==math.floor(-23.5)) + for i=1,100 do local a=-0; y=math.floor(a) end assert(y==math.floor(-0)) + for i=1,100 do local a=23; y=math.ceil(a) end assert(y==math.ceil(23)) + for i=1,100 do local a=23.5; y=math.ceil(a) end assert(y==math.ceil(23.5)) + for i=1,100 do local a=-23; y=math.ceil(a) end assert(y==math.ceil(-23)) + for i=1,100 do local a=-23.5; y=math.ceil(a) end assert(y==math.ceil(-23.5)) + for i=1,100 do local a=-0; y=math.ceil(a) end assert(y==math.ceil(-0)) +end + +do + local y = 0 + for i=1,100 do local a=23; y=math.sqrt(a) end assert(y==math.sqrt(23)) + for i=1,100 do local a=23; y=math.exp(a) end assert(y==math.exp(23)) + for i=1,100 do local a=23; y=math.log(a) end assert(y==math.log(23)) + for i=1,100 do local a=23; y=math.log10(a) end assert(y==math.log10(23)) + for i=1,100 do local a=23; y=math.sin(a) end assert(y==math.sin(23)) + for i=1,100 do local a=23; y=math.cos(a) end assert(y==math.cos(23)) + for i=1,100 do local a=23; y=math.tan(a) end assert(y==math.tan(23)) +end + +assert((10^-2 - 0.01) == 0) diff --git a/test/misc/libfuncs.lua b/test/misc/libfuncs.lua new file mode 100644 index 0000000000..d0cf42ff07 --- /dev/null +++ b/test/misc/libfuncs.lua @@ -0,0 +1,63 @@ + +local function check(m, expected) + local t = {} + for k in pairs(m) do t[#t+1] = tostring(k) end + table.sort(t) + local got = table.concat(t, ":") + if got ~= expected then + error("got: \""..got.."\"\nexpected: \""..expected.."\"", 2) + end +end + +local bit = bit +_G.bit = nil +_G.jit = nil +table.setn = nil +package.searchpath = nil + +if os.getenv("LUA52") then + check(_G, "_G:_VERSION:arg:assert:collectgarbage:coroutine:debug:dofile:error:gcinfo:getfenv:getmetatable:io:ipairs:load:loadfile:loadstring:math:module:newproxy:next:os:package:pairs:pcall:print:rawequal:rawget:rawlen:rawset:require:select:setfenv:setmetatable:string:table:tonumber:tostring:type:unpack:xpcall") + check(math, "abs:acos:asin:atan:atan2:ceil:cos:cosh:deg:exp:floor:fmod:frexp:huge:ldexp:log:log10:max:min:modf:pi:pow:rad:random:randomseed:sin:sinh:sqrt:tan:tanh") + check(string, "byte:char:dump:find:format:gmatch:gsub:len:lower:match:rep:reverse:sub:upper") + check(table, "concat:foreach:foreachi:getn:insert:maxn:pack:remove:sort:unpack") +else + check(_G, "_G:_VERSION:arg:assert:collectgarbage:coroutine:debug:dofile:error:gcinfo:getfenv:getmetatable:io:ipairs:load:loadfile:loadstring:math:module:newproxy:next:os:package:pairs:pcall:print:rawequal:rawget:rawset:require:select:setfenv:setmetatable:string:table:tonumber:tostring:type:unpack:xpcall") + check(math, "abs:acos:asin:atan:atan2:ceil:cos:cosh:deg:exp:floor:fmod:frexp:huge:ldexp:log:log10:max:min:mod:modf:pi:pow:rad:random:randomseed:sin:sinh:sqrt:tan:tanh") + check(string, "byte:char:dump:find:format:gfind:gmatch:gsub:len:lower:match:rep:reverse:sub:upper") + check(table, "concat:foreach:foreachi:getn:insert:maxn:remove:sort") +end + +check(io, "close:flush:input:lines:open:output:popen:read:stderr:stdin:stdout:tmpfile:type:write") + +check(debug.getmetatable(io.stdin), "__gc:__index:__tostring:close:flush:lines:read:seek:setvbuf:write") + +check(os, "clock:date:difftime:execute:exit:getenv:remove:rename:setlocale:time:tmpname") + +if os.getenv("LUA52") then + check(debug, "debug:getfenv:gethook:getinfo:getlocal:getmetatable:getregistry:getupvalue:getuservalue:setfenv:sethook:setlocal:setmetatable:setupvalue:setuservalue:traceback:upvalueid:upvaluejoin") +else + check(debug, "debug:getfenv:gethook:getinfo:getlocal:getmetatable:getregistry:getupvalue:setfenv:sethook:setlocal:setmetatable:setupvalue:traceback:upvalueid:upvaluejoin") +end + +check(package, "config:cpath:loaded:loaders:loadlib:path:preload:seeall") + +check(package.loaders, "1:2:3:4") +package.loaded.bit = nil +package.loaded.jit = nil +package.loaded["jit.util"] = nil +package.loaded["jit.opt"] = nil +check(package.loaded, "_G:coroutine:debug:io:math:os:package:string:table") + +if bit then + check(bit, "arshift:band:bnot:bor:bswap:bxor:lshift:rol:ror:rshift:tobit:tohex") +end + +local ok, ffi = pcall(require, "ffi") +if ok then + check(ffi, "C:abi:alignof:arch:cast:cdef:copy:errno:fill:gc:istype:load:metatype:new:offsetof:os:sizeof:string:typeinfo:typeof") +end + +assert(math.pi == 3.141592653589793) +assert(math.huge > 0 and 1/math.huge == 0) +assert(debug.getmetatable("").__index == string) + diff --git a/test/misc/lightud.lua b/test/misc/lightud.lua new file mode 100644 index 0000000000..4974d50fcb --- /dev/null +++ b/test/misc/lightud.lua @@ -0,0 +1,88 @@ +local ctest = require("ctest") + +local lightud = ctest.lightud +local assert = assert + +-- x64 lightud tests +if jit and jit.arch == "x64" then + do + local ud1 = lightud(0x12345678) + local ud2 = lightud(0x12345678) + assert(ud1 == ud2) + assert(tostring(ud1) == "userdata: 0x12345678") + end + do + local ud1 = lightud(1) + local ud2 = lightud(2) + assert(ud1 ~= ud2) + end + do + local ud1 = lightud(2^47-1) + local ud2 = lightud(2^47-1) + assert(ud1 == ud2) + assert(tostring(ud1) == "userdata: 0x7fffffffffff") + end + do + local ud1 = lightud(0x12345678+123*2^32) + local ud2 = lightud(0x12345678+456*2^32) + for i=1,100 do assert(ud1 ~= ud2) end + end + assert(tostring(lightud(0x5abc*2^32 + 0xdef01234)) == "userdata: 0x5abcdef01234") + assert(pcall(lightud, 2^47) == false) + assert(pcall(lightud, 2^64-2048) == false) +end + +assert(getmetatable(lightud(1)) == nil) + +-- lightuserdata SLOAD value and HREF key +do + local ud = lightud(12345) + local t = {[ud] = 42} + for i=1,100 do + assert(t[ud] == 42) + end +end + +-- lightuserdata NEWREF key +do + local ud = lightud(12345) + for i=1,100 do + local t = {[ud] = 42} + assert(t[ud] == 42) + end +end + +-- lightuserdata ASTORE/HSTORE value +do + local ud = lightud(12345) + local t = {} + for i=1,100 do + t[i] = ud + end + assert(t[100] == ud) +end + +-- lightuserdata sync to stack +do + local ud = lightud(12345) + local x = nil + for j=1,20 do + for i=1,50 do + x = ud + end + assert(x == ud) + end +end + +-- lightuserdata vs. number type check +do + local t = {} + for i=1,200 do t[i] = i end + t[180] = lightud(12345) + local x = 0 + assert(not pcall(function(t) + for i=1,200 do x = x + t[i] end + end, t)) + assert(x == 16110) +end + diff --git a/test/misc/loop_unroll.lua b/test/misc/loop_unroll.lua new file mode 100644 index 0000000000..1700fac910 --- /dev/null +++ b/test/misc/loop_unroll.lua @@ -0,0 +1,35 @@ + +-- type instability on loop unroll -> record unroll +do + local flip = true + for i=1,100 do flip = not flip end + assert(flip == true) +end + +do + local t = {} + local a, b, c = 1, "", t + for i=1,100 do a,b,c=b,c,a end + assert(c == 1 and a == "" and b == t) +end + +-- FAILFOLD on loop unroll -> LJ_TRERR_GFAIL -> record unroll +do + local t = { 1, 2 } + local k = 2 + local x = 0 + for i=1,200 do + x = x + t[k] + k = k == 1 and 2 or 1 + end + assert(x == 300 and k == 2) +end + +-- Unroll if inner loop aborts. +local j = 0 +for i = 1,100 do + repeat + j = j+1 + until true +end + diff --git a/test/misc/math_random.lua b/test/misc/math_random.lua new file mode 100644 index 0000000000..391ee2d901 --- /dev/null +++ b/test/misc/math_random.lua @@ -0,0 +1,21 @@ + +local random = math.random +local randomseed = math.randomseed + +do + local N = 1000 + local min, max = math.min, math.max + for j=1,100 do + randomseed(j) + local lo, hi, sum = math.huge, -math.huge, 0 + for i=1,N do + local x = random() + sum = sum + x + lo = min(lo, x) + hi = max(hi, x) + end + assert(lo*N < 15 and (1-hi)*N < 15) + assert(sum > N*0.45 and sum < N*0.55) + end +end + diff --git a/test/misc/meta_arith.lua b/test/misc/meta_arith.lua new file mode 100644 index 0000000000..f9028fcd0a --- /dev/null +++ b/test/misc/meta_arith.lua @@ -0,0 +1,103 @@ + +local function create(arith, v1, v2) + local meta = { + __add=function(a,b) return arith("add", a, b) end, + __sub=function(a,b) return arith("sub", a, b) end, + __mul=function(a,b) return arith("mul", a, b) end, + __div=function(a,b) return arith("div", a, b) end, + __mod=function(a,b) return arith("mod", a, b) end, + __pow=function(a,b) return arith("pow", a, b) end, + __unm=function(a,b) return arith("unm", a, b) end, + } + return setmetatable({v1}, meta), setmetatable({v2}, meta) +end + +local a, b = create(function(op,a,b) return op end) +assert(a+b == "add") +assert(a-b == "sub") +assert(a*b == "mul") +assert(a/b == "div") +assert(a%b == "mod") +assert(a^b == "pow") +assert(-a == "unm") + +local a, b = create(function(op,a,b) return a[1] end, "foo", 42) +assert(a+b == "foo") +assert(a-b == "foo") +assert(a*b == "foo") +assert(a/b == "foo") +assert(a%b == "foo") +assert(a^b == "foo") +assert(-a == "foo") + +local a, b = create(function(op,a,b) return b[1] end, 42, "foo") +assert(a+b == "foo") +assert(a-b == "foo") +assert(a*b == "foo") +assert(a/b == "foo") +assert(a%b == "foo") +assert(a^b == "foo") +assert(-a == 42) + + +local a, b = create(function(op,a,b) return a[1]+b end, 39), 3 +assert(a+b == 42) +assert(a-b == 42) +assert(a*b == 42) +assert(a/b == 42) +assert(a%b == 42) +assert(a^b == 42) + +local a, b = 39, create(function(op,a,b) return a+b[1] end, 3) +assert(a+b == 42) +assert(a-b == 42) +assert(a*b == 42) +assert(a/b == 42) +assert(a%b == 42) +assert(a^b == 42) + + +local a, b = "39", 3 +assert(a+b == 42) +assert(a-b == 36) +assert(a*b == 117) +assert(a/b == 13) +assert(a%b == 0) +assert(a^b == 59319) +assert(-a == -39) + +local a, b = 39, "3" +assert(a+b == 42) +assert(a-b == 36) +assert(a*b == 117) +assert(a/b == 13) +assert(a%b == 0) +assert(a^b == 59319) +assert(-a == -39) + +local a, b = "39", "3" +assert(a+b == 42) +assert(a-b == 36) +assert(a*b == 117) +assert(a/b == 13) +assert(a%b == 0) +assert(a^b == 59319) +assert(-a == -39) + + +local a = "39" +assert(a+3 == 42) +assert(a-3 == 36) +assert(a*3 == 117) +assert(a/3 == 13) +assert(a%3 == 0) +assert(a^3 == 59319) + +local b = "3" +assert(39+b == 42) +assert(39-b == 36) +assert(39*b == 117) +assert(39/b == 13) +assert(39%b == 0) +assert(39^b == 59319) + diff --git a/test/misc/meta_arith_jit.lua b/test/misc/meta_arith_jit.lua new file mode 100644 index 0000000000..485cebf53e --- /dev/null +++ b/test/misc/meta_arith_jit.lua @@ -0,0 +1,68 @@ + +do + local t = {} + local mt = { + __add = function(a, b) assert(b == t); return a+11 end, + __sub = function(a, b) assert(b == t); return a+12 end, + __mul = function(a, b) assert(b == t); return a+13 end, + __div = function(a, b) assert(b == t); return a+14 end, + __mod = function(a, b) assert(b == t); return a+15 end, + __pow = function(a, b) assert(b == t); return a+16 end, + __unm = function(a, b) assert(a == t and b == t); return 17 end, + } + t = setmetatable(t, mt) + do local x = 0; for i=1,100 do x = x + t end; assert(x == 1100); end + do local x = 0; for i=1,100 do x = x - t end; assert(x == 1200); end + do local x = 0; for i=1,100 do x = x * t end; assert(x == 1300); end + do local x = 0; for i=1,100 do x = x / t end; assert(x == 1400); end + do local x = 0; for i=1,100 do x = x % t end; assert(x == 1500); end + do local x = 0; for i=1,100 do x = x ^ t end; assert(x == 1600); end + do local x = 0; for i=1,100 do x = x + (-t) end; assert(x == 1700); end +end + +do + local t = {} + local mt = { + __add = function(a, b) assert(a == t); return b+11 end, + __sub = function(a, b) assert(a == t); return b+12 end, + __mul = function(a, b) assert(a == t); return b+13 end, + __div = function(a, b) assert(a == t); return b+14 end, + __mod = function(a, b) assert(a == t); return b+15 end, + __pow = function(a, b) assert(a == t); return b+16 end, + } + t = setmetatable(t, mt) + do local x = 0; for i=1,100 do x = t + x end; assert(x == 1100); end + do local x = 0; for i=1,100 do x = t - x end; assert(x == 1200); end + do local x = 0; for i=1,100 do x = t * x end; assert(x == 1300); end + do local x = 0; for i=1,100 do x = t / x end; assert(x == 1400); end + do local x = 0; for i=1,100 do x = t % x end; assert(x == 1500); end + do local x = 0; for i=1,100 do x = t ^ x end; assert(x == 1600); end +end + +do + local t = {} + local mt = { + __add = function(a, b) assert(a == t and b == t); return 11 end, + __sub = function(a, b) assert(a == t and b == t); return 12 end, + __mul = function(a, b) assert(a == t and b == t); return 13 end, + __div = function(a, b) assert(a == t and b == t); return 14 end, + __mod = function(a, b) assert(a == t and b == t); return 15 end, + __pow = function(a, b) assert(a == t and b == t); return 16 end, + } + t = setmetatable(t, mt) + do local x = 0; for i=1,100 do x = t + t end; assert(x == 11); end + do local x = 0; for i=1,100 do x = t - t end; assert(x == 12); end + do local x = 0; for i=1,100 do x = t * t end; assert(x == 13); end + do local x = 0; for i=1,100 do x = t / t end; assert(x == 14); end + do local x = 0; for i=1,100 do x = t % t end; assert(x == 15); end + do local x = 0; for i=1,100 do x = t ^ t end; assert(x == 16); end +end + +do + local t = {} + local mt = { __add = function(a, b) end } + t = setmetatable(t, mt) + local x + for i=1,100 do x = t+t end + assert(x == nil) +end diff --git a/test/misc/meta_call.lua b/test/misc/meta_call.lua new file mode 100644 index 0000000000..5cc22be109 --- /dev/null +++ b/test/misc/meta_call.lua @@ -0,0 +1,78 @@ + +local function callmeta(o, a, b) + return o, a, b +end + +local meta = { __call = callmeta } + +do + local t = setmetatable({}, meta) + local o,a,b = t() + assert(o == t and a == nil and b == nil) + local o,a,b = t("foo") + assert(o == t and a == "foo" and b == nil) + local o,a,b = t("foo", "bar") + assert(o == t and a == "foo" and b == "bar") +end + +do + local u = newproxy(true) + getmetatable(u).__call = callmeta + + local o,a,b = u() + assert(o == u and a == nil and b == nil) + local o,a,b = u("foo") + assert(o == u and a == "foo" and b == nil) + local o,a,b = u("foo", "bar") + assert(o == u and a == "foo" and b == "bar") +end + +debug.setmetatable(0, meta) +local o,a,b = (42)() +assert(o == 42 and a == nil and b == nil) +local o,a,b = (42)("foo") +assert(o == 42 and a == "foo" and b == nil) +local o,a,b = (42)("foo", "bar") +assert(o == 42 and a == "foo" and b == "bar") +debug.setmetatable(0, nil) + +do + local tc = setmetatable({}, { __call = function(o,a,b) return o end}) + local ta = setmetatable({}, { __add = tc}) + local o,a = ta + ta + assert(o == tc and a == nil) + + getmetatable(tc).__call = function(o,a,b) return a end + local o,a = ta + ta + assert(o == ta and a == nil) +end + +do + local t = setmetatable({}, { __call = function(t, a) return 100-a end }) + for i=1,100 do assert(t(i) == 100-i) end +end + +do + local t = setmetatable({}, { __call = rawget }) + for i=1,100 do t[i] = 100-i end + for i=1,100 do assert(t(i) == 100-i) end +end + +debug.setmetatable(0, { __call = function(n) return 100-n end }) +for i=1,100 do assert((i)() == 100-i) end +debug.setmetatable(0, nil) + +do + local t = setmetatable({}, { __newindex = pcall, __call = rawset }) + for i=1,100 do t[i] = 100-i end + for i=1,100 do assert(t[i] == 100-i) end +end + +do + local t = setmetatable({}, { + __index = pcall, __newindex = rawset, + __call = function(t, i) t[i] = 100-i end, + }) + for i=1,100 do assert(t[i] == true and rawget(t, i) == 100-i) end +end + diff --git a/test/misc/meta_cat.lua b/test/misc/meta_cat.lua new file mode 100644 index 0000000000..6f9e9fff39 --- /dev/null +++ b/test/misc/meta_cat.lua @@ -0,0 +1,62 @@ + +do + local a, b, c = "foo", "bar", "baz" + assert(a..b == "foobar") + assert(a..b..c == "foobarbaz") +end + +local function create(cat, v1, v2) + local meta = { __concat = cat } + return setmetatable({v1}, meta), setmetatable({v2}, meta) +end + +do + local a, b = create(function(a, b) return a end) + assert(a..b == a) + assert(b..a == b) + assert(a..b..b == a) + assert(a..a..b == a) + assert(a..b..a == a) + assert(a..b..b..b..b..b..b..b == a) +end + +do + local a, b = create(function(a, b) return b end) + assert(a..b == b) + assert(b..a == a) + assert(a..b..b == b) + assert(a..a..b == b) + assert(b..b..a == a) + assert(a..a..a..a..a..a..a..b == b) +end + +do + local a, b = create(function(a, b) + return (type(a) == "string" and a or a[1]).. + (type(b) == "string" and b or b[1]) + end, "a", "b") + assert(a..b == "ab") + assert(a..b == "ab") + assert(a..b..b == "abb") + assert(a..b..a == "aba") + assert(a..a..a..a..a..a..a..b == "aaaaaaab") + assert(a..a..a.."x".."x"..a..a..b == "aaaxxaab") + assert("x"..a..a..a..a..a..a..b == "xaaaaaab") + assert(a..b..a..b..a.."x".."x".."x" == "ababaxxx") +end + +do + local a, b = create(function(a, b) + if a ~= b then local x = gg end + return (type(a) == "string" and a or a[1]).. + (type(b) == "string" and b or b[1]) + end, "a", "b") + local y + for i=1,100 do y = a..b end + assert(y == "ab") + for i=1,100 do y = a..b.."x" end + assert(y == "abx") + for i=1,100 do y = a..b.. 1 .. "z" end + assert(y == "ab1z") +end + diff --git a/test/misc/meta_comp.lua b/test/misc/meta_comp.lua new file mode 100644 index 0000000000..e265bef2c5 --- /dev/null +++ b/test/misc/meta_comp.lua @@ -0,0 +1,151 @@ + +local function create(comp, v1, v2) + local meta = { + __lt=function(a,b) return comp("lt", a, b) end, + __le=function(a,b) return comp("le", a, b) end, + } + return setmetatable({v1}, meta), setmetatable({v2}, meta) +end + +local xop +local a, b = create(function(op,a,b) xop = op; return "" end) +assert(ab == true and xop == "lt"); xop = nil +assert(a<=b == true and xop == "le"); xop = nil +assert(a>=b == true and xop == "le"); xop = nil + +assert(not (ab) == false and xop == "lt"); xop = nil +assert(not (a<=b) == false and xop == "le"); xop = nil +assert(not (a>=b) == false and xop == "le"); xop = nil + +-- __le metamethod is optional and substituted with arg+res inverted __lt. +local f = getmetatable(a).__le +getmetatable(a).__le = nil +assert(ab == true and xop == "lt"); xop = nil +assert(a<=b == false and xop == "lt"); xop = nil +assert(a>=b == false and xop == "lt"); xop = nil + +assert(not (ab) == false and xop == "lt"); xop = nil +assert(not (a<=b) == true and xop == "lt"); xop = nil +assert(not (a>=b) == true and xop == "lt"); xop = nil +getmetatable(a).__le = f + +-- Different metatable, but same metamethod works, too. +setmetatable(b, { __lt = getmetatable(b).__lt, __le = getmetatable(b).__le }) +assert(ab == true and xop == "lt"); xop = nil +assert(a<=b == true and xop == "le"); xop = nil +assert(a>=b == true and xop == "le"); xop = nil + +assert(not (ab) == false and xop == "lt"); xop = nil +assert(not (a<=b) == false and xop == "le"); xop = nil +assert(not (a>=b) == false and xop == "le"); xop = nil + +local a, b = create(function(op,a,b) + if op == "lt" then return a[1]b == false) +assert(a<=b == true) +assert(a>=b == false) + +assert(not (ab) == true) +assert(not (a<=b) == false) +assert(not (a>=b) == true) + +b[1] = 1 +assert(ab == false) +assert(a<=b == true) +assert(a>=b == true) + +assert(not (ab) == true) +assert(not (a<=b) == false) +assert(not (a>=b) == false) + +a[1] = 2 +assert(ab == true) +assert(a<=b == false) +assert(a>=b == true) + +assert(not (ab) == false) +assert(not (a<=b) == true) +assert(not (a>=b) == false) + +-- __le metamethod is optional and substituted with arg+res inverted __lt. +getmetatable(a).__le = nil +a[1] = 1 +b[1] = 2 +assert(ab == false) +assert(a<=b == true) +assert(a>=b == false) + +assert(not (ab) == true) +assert(not (a<=b) == false) +assert(not (a>=b) == true) + +b[1] = 1 +assert(ab == false) +assert(a<=b == true) +assert(a>=b == true) + +assert(not (ab) == true) +assert(not (a<=b) == false) +assert(not (a>=b) == false) + +a[1] = 2 +assert(ab == true) +assert(a<=b == false) +assert(a>=b == true) + +assert(not (ab) == false) +assert(not (a<=b) == true) +assert(not (a>=b) == false) + +-- String comparisons: +local function str_cmp(a, b, lt, gt, le, ge) + assert(ab == gt) + assert(a<=b == le) + assert(a>=b == ge) + assert((not (ab)) == (not gt)) + assert((not (a<=b)) == (not le)) + assert((not (a>=b)) == (not ge)) +end + +local function str_lo(a, b) + str_cmp(a, b, true, false, true, false) +end + +local function str_eq(a, b) + str_cmp(a, b, false, false, true, true) +end + +local function str_hi(a, b) + str_cmp(a, b, false, true, false, true) +end + +str_lo("a", "b") +str_eq("a", "a") +str_hi("b", "a") + +str_lo("a", "aa") +str_hi("aa", "a") + +str_lo("a", "a\0") +str_hi("a\0", "a") + diff --git a/test/misc/meta_comp_jit.lua b/test/misc/meta_comp_jit.lua new file mode 100644 index 0000000000..57f711283e --- /dev/null +++ b/test/misc/meta_comp_jit.lua @@ -0,0 +1,104 @@ + +local lt, le = false, false +local t, u = {}, {} +local x, ax, bx +local function ck(xx, a, b) + if x ~= xx then error("bad x", 2) end + if ax ~= a then error("bad ax", 2) end + if bx ~= b then error("bad bx", 2) end +end +local mt = { + __lt = function(a, b) ax=a; bx=b; return lt end, + __le = function(a, b) ax=a; bx=b; return le end, +} +t = setmetatable(t, mt) +u = setmetatable(u, mt) +lt, le = false, false +x = 0; for i=1,100 do x = t < u and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = t <= u and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = t > u and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = t >= u and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = not (t < u) and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = not (t <= u) and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = not (t > u) and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = not (t >= u) and 2 or 1 end ck(2, u, t) +lt, le = false, true +x = 0; for i=1,100 do x = t < u and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = t <= u and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = t > u and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = t >= u and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = not (t < u) and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = not (t <= u) and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = not (t > u) and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = not (t >= u) and 2 or 1 end ck(1, u, t) +lt, le = true, false +x = 0; for i=1,100 do x = t < u and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = t <= u and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = t > u and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = t >= u and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = not (t < u) and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = not (t <= u) and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = not (t > u) and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = not (t >= u) and 2 or 1 end ck(2, u, t) +lt, le = true, true +x = 0; for i=1,100 do x = t < u and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = t <= u and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = t > u and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = t >= u and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = not (t < u) and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = not (t <= u) and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = not (t > u) and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = not (t >= u) and 2 or 1 end ck(1, u, t) +mt.__le = nil +lt = false +x = 0; for i=1,100 do x = t < u and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = t <= u and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = t > u and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = t >= u and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = not (t < u) and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = not (t <= u) and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = not (t > u) and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = not (t >= u) and 2 or 1 end ck(1, t, u) +lt = true +x = 0; for i=1,100 do x = t < u and 2 or 1 end ck(2, t, u) +x = 0; for i=1,100 do x = t <= u and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = t > u and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = t >= u and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = not (t < u) and 2 or 1 end ck(1, t, u) +x = 0; for i=1,100 do x = not (t <= u) and 2 or 1 end ck(2, u, t) +x = 0; for i=1,100 do x = not (t > u) and 2 or 1 end ck(1, u, t) +x = 0; for i=1,100 do x = not (t >= u) and 2 or 1 end ck(2, t, u) + +-- Mixed metamethods for ordered comparisons. +do + local mt1 = { __lt = function(a, b) return a[1] < b[1] end } + local mt2 = { __lt = function(a, b) return a[1] < b[1] end } + local t1 = setmetatable({1}, mt1) + local t2 = setmetatable({2}, mt2) + do + local x + for i=1,100 do x = t1 <= t1 end + assert(x == true) + end + local ok, ret = pcall(function() + local x + for i=1,100 do x = t1 < t2 end + return x + end) + if os.getenv("LUA52") then + assert(ok and ret == true) + else + assert(not ok) + end + local ok, ret = pcall(function() + local x + for i=1,100 do x = t1 <= t2 end + return x + end) + if os.getenv("LUA52") then + assert(ok and ret == true) + else + assert(not ok) + end +end + diff --git a/test/misc/meta_eq.lua b/test/misc/meta_eq.lua new file mode 100644 index 0000000000..786f480423 --- /dev/null +++ b/test/misc/meta_eq.lua @@ -0,0 +1,66 @@ + +local function create(equal, v1, v2) + local meta = { __eq = equal } + return setmetatable({v1}, meta), setmetatable({v2}, meta) +end + +local xop +local a, b = create(function(a,b) xop = "eq" return "" end) +assert(a==b == true and xop == "eq"); xop = nil +assert(a~=b == false and xop == "eq"); xop = nil + +-- Different metatable, but same metamethod works, too. +setmetatable(b, { __eq = getmetatable(b).__eq }) +assert(a==b == true and xop == "eq"); xop = nil +assert(a~=b == false and xop == "eq"); xop = nil + +local a, b = create(function(a,b) return a[1] == b[1] end, 1, 2) +assert(a==b == false) +assert(a~=b == true) + +b[1] = 1 +assert(a==b == true) +assert(a~=b == false) + +a[1] = 2 +assert(a==b == false) +assert(a~=b == true) + + +local function obj_eq(a, b) + assert(a==b == true) + assert(a~=b == false) +end + +local function obj_ne(a, b) + assert(a==b == false) + assert(a~=b == true) +end + +obj_eq(nil, nil) +obj_ne(nil, false) +obj_ne(nil, true) + +obj_ne(false, nil) +obj_eq(false, false) +obj_ne(false, true) + +obj_ne(true, nil) +obj_ne(true, false) +obj_eq(true, true) + +obj_eq(1, 1) +obj_ne(1, 2) +obj_ne(2, 1) + +obj_eq("a", "a") +obj_ne("a", "b") +obj_ne("a", 1) +obj_ne(1, "a") + +local t, t2 = {}, {} +obj_eq(t, t) +obj_ne(t, t2) +obj_ne(t, 1) +obj_ne(t, "") + diff --git a/test/misc/meta_eq_jit.lua b/test/misc/meta_eq_jit.lua new file mode 100644 index 0000000000..ad5d68f18b --- /dev/null +++ b/test/misc/meta_eq_jit.lua @@ -0,0 +1,36 @@ + +do + local eq = false + local t, u = {}, {} + local x, ax, bx + local function ck(xx, a, b) + if x ~= xx then error("bad x", 2) end + if ax ~= a then error("bad ax", 2) end + if bx ~= b then error("bad bx", 2) end + end + local mt = { + __eq = function(a, b) ax=a; bx=b; return eq end, + } + t = setmetatable(t, mt) + u = setmetatable(u, mt) + eq = false + x = 0; for i=1,100 do x = t == u and 2 or 1 end ck(1, t, u) + x = 0; for i=1,100 do x = t ~= u and 2 or 1 end ck(2, t, u) + x = 0; for i=1,100 do x = not (t == u) and 2 or 1 end ck(2, t, u) + x = 0; for i=1,100 do x = not (t ~= u) and 2 or 1 end ck(1, t, u) + eq = true + x = 0; for i=1,100 do x = t == u and 2 or 1 end ck(2, t, u) + x = 0; for i=1,100 do x = t ~= u and 2 or 1 end ck(1, t, u) + x = 0; for i=1,100 do x = not (t == u) and 2 or 1 end ck(1, t, u) + x = 0; for i=1,100 do x = not (t ~= u) and 2 or 1 end ck(2, t, u) +end + +do + local bit = require("bit") + local mt = { __eq = function(a, b) return true end } + local tt = { [0] = setmetatable({}, mt), setmetatable({}, mt) } + for i=0,100 do + assert(tt[0] == tt[bit.band(i, 1)]) + end +end + diff --git a/test/misc/meta_framegap.lua b/test/misc/meta_framegap.lua new file mode 100644 index 0000000000..4f2f719ae2 --- /dev/null +++ b/test/misc/meta_framegap.lua @@ -0,0 +1,23 @@ + +local t = setmetatable({}, { __add = function(a, b) + if b > 200 then + for j=1,10 do end + return b+3 + elseif b > 100 then + return b+2 + else + return b+1 + end +end }) + +local function f(t, i) + do return t+i end + -- Force large frame with unassigned slots below mm. + do local a,b,c,d,e,f,g,h,i,j,k end +end + +local x = 0 +for i=1,300 do + x = f(t, i) +end +assert(x == 303) diff --git a/test/misc/meta_getset.lua b/test/misc/meta_getset.lua new file mode 100644 index 0000000000..3f5c1da19c --- /dev/null +++ b/test/misc/meta_getset.lua @@ -0,0 +1,34 @@ + +do + local t = setmetatable({}, { __metatable = "foo" }) + for i=1,100 do assert(getmetatable(t) == "foo") end +end + +do + local mt = {} + local t = setmetatable({}, mt) + for i=1,100 do assert(getmetatable(t) == mt) end + for i=1,100 do assert(setmetatable(t, mt) == t) end +end + +do + local mt = {} + local t = {} + for i=1,200 do t[i] = setmetatable({}, mt) end + t[150] = setmetatable({}, { __metatable = "foo" }) + for i=1,200 do + if not pcall(setmetatable, t[i], mt) then assert(i == 150) end + end + for i=1,200 do assert(getmetatable(t[i]) == mt or i == 150) end + for i=1,200 do + if not pcall(setmetatable, t[i], nil) then assert(i == 150) end + end + for i=1,200 do assert(getmetatable(t[i]) == nil or i == 150) end +end + +do + local x = true + for i=1,100 do x = getmetatable(i) end + assert(x == nil) +end + diff --git a/test/misc/meta_len.lua b/test/misc/meta_len.lua new file mode 100644 index 0000000000..6e77678de5 --- /dev/null +++ b/test/misc/meta_len.lua @@ -0,0 +1,43 @@ + +local lua52 = os.getenv("LUA52") + +local mt = { __len = function(o, o2) + if lua52 then + assert(o2 == o) + else + assert(o2 == nil) + end + return 42 +end } + +do + local t = {1,2,3} + assert(#t == 3) + assert(#"abcdef" == 6) + + setmetatable(t, { __foo = function() end }) + assert(#t == 3) + assert(#t == 3) + + setmetatable(t, mt) + if lua52 then + assert(#t == 42) -- __len DOES work on tables. + assert(rawlen(t) == 3) + else + assert(#t == 3) -- __len does NOT work on tables. + end +end + +do + local u = newproxy(true) + getmetatable(u).__len = function(o) return 42 end + assert(#u == 42) + local x = 0 + for i=1,100 do x = x + #u end + assert(x == 4200) +end + +debug.setmetatable(0, mt) +assert(#1 == 42) +debug.setmetatable(0, nil) + diff --git a/test/misc/meta_nomm.lua b/test/misc/meta_nomm.lua new file mode 100644 index 0000000000..457d0ee288 --- /dev/null +++ b/test/misc/meta_nomm.lua @@ -0,0 +1,21 @@ + +do + local keys = {} + for i=1,100 do keys[i] = "foo" end + keys[95] = "__index" + local function fidx(t, k) return 12345 end + local mt = { foo = 1, __index = "" } + local t = setmetatable({ 1 }, mt) + t[1] = nil + mt.__index = nil + local x = nil + for i=1,100 do + mt[keys[i]] = fidx + if t[1] then + if not x then x = i end + assert(t[1] == 12345) + end + end + assert(x == 95) +end + diff --git a/test/misc/meta_pairs.lua b/test/misc/meta_pairs.lua new file mode 100644 index 0000000000..802a56ff6d --- /dev/null +++ b/test/misc/meta_pairs.lua @@ -0,0 +1,47 @@ + +do + local t = {} + for i=1,10 do t[i] = i+100 end + local a, b = 0, 0 + for j=1,100 do for k,v in ipairs(t) do a = a + k; b = b + v end end + assert(a == 5500) + assert(b == 105500) + a, b = 0, 0 + for j=1,100 do for k,v in pairs(t) do a = a + k; b = b + v end end + assert(a == 5500) + assert(b == 105500) +end + +do + local t = setmetatable({}, {}) + for i=1,10 do t[i] = i+100 end + local a, b = 0, 0 + for j=1,100 do for k,v in ipairs(t) do a = a + k; b = b + v end end + assert(a == 5500) + assert(b == 105500) + a, b = 0, 0 + for j=1,100 do for k,v in pairs(t) do a = a + k; b = b + v end end + assert(a == 5500) + assert(b == 105500) +end + +if os.getenv("LUA52") then + local function iter(t, i) + i = i + 1 + if t[i] then return i, t[i]+2 end + end + local function itergen(t) + return iter, t, 0 + end + local t = setmetatable({}, { __pairs = itergen, __ipairs = itergen }) + for i=1,10 do t[i] = i+100 end + local a, b = 0, 0 + for j=1,100 do for k,v in ipairs(t) do a = a + k; b = b + v end end + assert(a == 5500) + assert(b == 107500) + a, b = 0, 0 + for j=1,100 do for k,v in pairs(t) do a = a + k; b = b + v end end + assert(a == 5500) + assert(b == 107500) +end + diff --git a/test/misc/meta_tget.lua b/test/misc/meta_tget.lua new file mode 100644 index 0000000000..7c051009ec --- /dev/null +++ b/test/misc/meta_tget.lua @@ -0,0 +1,25 @@ + +local t=setmetatable({}, {__index=function(t,k) + return 100-k +end}) + +for i=1,100 do assert(t[i] == 100-i) end + +for i=1,100 do t[i] = i end +for i=1,100 do assert(t[i] == i) end + +for i=1,100 do t[i] = nil end +for i=1,100 do assert(t[i] == 100-i) end + + +local x +local t2=setmetatable({}, {__index=function(t,k) + x = k +end}) + +assert(t2[1] == nil) +assert(x == 1) + +assert(t2.foo == nil) +assert(x == "foo") + diff --git a/test/misc/meta_tget_nontab.lua b/test/misc/meta_tget_nontab.lua new file mode 100644 index 0000000000..3c40a514de --- /dev/null +++ b/test/misc/meta_tget_nontab.lua @@ -0,0 +1,33 @@ + +do + local u = newproxy(true) + getmetatable(u).__index = { foo = u, bar = 42 } + + local x = 0 + for i=1,100 do + x = x + u.bar + u = u.foo + end + assert(x == 4200) + + x = 0 + for i=1,100 do + u = u.foo + x = x + u.bar + end + assert(x == 4200) +end + +do + local s = "foo" + string.s = s + local x = 0 + local t = {} + for i=1,100 do + x = x + s:len() + s = s.s + t[s] = t -- Hash store with same type prevents hoisting + end + assert(x == 300) +end + diff --git a/test/misc/meta_tset.lua b/test/misc/meta_tset.lua new file mode 100644 index 0000000000..f844e1b567 --- /dev/null +++ b/test/misc/meta_tset.lua @@ -0,0 +1,15 @@ + +local t=setmetatable({}, {__newindex=function(t,k,v) + rawset(t, k, 100-v) +end}) + +for i=1,100 do t[i] = i end +for i=1,100 do assert(t[i] == 100-i) end + +for i=1,100 do t[i] = i end +for i=1,100 do assert(t[i] == i) end + +for i=1,100 do t[i] = nil end +for i=1,100 do t[i] = i end +for i=1,100 do assert(t[i] == 100-i) end + diff --git a/test/misc/meta_tset_nilget.lua b/test/misc/meta_tset_nilget.lua new file mode 100644 index 0000000000..1fdebab383 --- /dev/null +++ b/test/misc/meta_tset_nilget.lua @@ -0,0 +1,23 @@ + +do + local count = 0 + local t = setmetatable({ foo = nil }, + { __newindex=function() count = count + 1 end }) + for j=1,2 do + for i=1,100 do t.foo = 1 end + rawset(t, "foo", 1) + end + assert(count == 100) +end + +do + local count = 0 + local t = setmetatable({ nil }, + { __newindex=function() count = count + 1 end }) + for j=1,2 do + for i=1,100 do t[1] = 1 end + rawset(t, 1, 1) + end + assert(count == 100) +end + diff --git a/test/misc/meta_tset_resize.lua b/test/misc/meta_tset_resize.lua new file mode 100644 index 0000000000..a58dac1136 --- /dev/null +++ b/test/misc/meta_tset_resize.lua @@ -0,0 +1,10 @@ +local grandparent = {} +grandparent.__newindex = function(s,_,_) tostring(s) end + +local parent = {} +parent.__newindex = parent +parent.bar = 1 +setmetatable(parent, grandparent) + +local child = setmetatable({}, parent) +child.foo = _ diff --git a/test/misc/meta_tset_str.lua b/test/misc/meta_tset_str.lua new file mode 100644 index 0000000000..b72be3a8e5 --- /dev/null +++ b/test/misc/meta_tset_str.lua @@ -0,0 +1,18 @@ + +local t=setmetatable({}, {__newindex=function(t,k,v) + assert(v == "foo"..k) + rawset(t, k, "bar"..k) +end}) + +for i=1,100 do t[i]="foo"..i end +for i=1,100 do assert(t[i] == "bar"..i) end + +for i=1,100 do t[i]="baz"..i end +for i=1,100 do assert(t[i] == "baz"..i) end + +local t=setmetatable({foo=1,bar=1,baz=1},{}) +t.baz=nil +t.baz=2 +t.baz=nil +t.baz=2 + diff --git a/test/misc/modulo.lua b/test/misc/modulo.lua new file mode 100644 index 0000000000..fefb02cf1e --- /dev/null +++ b/test/misc/modulo.lua @@ -0,0 +1,42 @@ + +for x=-5,5 do + for y=-5,5 do + if y ~= 0 then + assert(x%y == x-math.floor(x/y)*y) + end + end +end + +for x=-5,5,0.25 do + for y=-5,5,0.25 do + if y ~= 0 then + assert(x%y == x-math.floor(x/y)*y) + end + end +end + +do + local y = 0 + for x=-100,123 do + y = y + x%17 + end + assert(y == 1777) +end + +do + local y = 0 + for x=-100,123 do + if x ~= 0 then + y = y + 85%x + end + end + assert(y == 2059) +end + +do + local x = 1%0 + assert(x ~= x) + x = math.floor(0/0) + assert(x ~= x) +end + diff --git a/test/misc/nsieve.lua b/test/misc/nsieve.lua new file mode 100644 index 0000000000..49dc1c8df7 --- /dev/null +++ b/test/misc/nsieve.lua @@ -0,0 +1,20 @@ +local function nsieve(m, isPrime) + for i=2,m do isPrime[i] = true end + local count = 0 + for i=2,m do + if isPrime[i] then + for k=i+i,m,i do isPrime[k] = false end + count = count + 1 + end + end + return count +end + +local flags = {} +if arg and arg[1] then + local N = tonumber(arg and arg[1]) + io.write(string.format("Primes up to %8d %8d", N, nsieve(N,flags)), "\n") +else + assert(nsieve(100, flags) == 25) + assert(nsieve(12345, flags) == 1474) +end diff --git a/test/misc/parse_andor.lua b/test/misc/parse_andor.lua new file mode 100644 index 0000000000..2c796cb375 --- /dev/null +++ b/test/misc/parse_andor.lua @@ -0,0 +1,58 @@ +local x = ((1 or false) and true) or false +assert(x == true) + +local basiccases = { + {"nil", nil}, + {"false", false}, + {"true", true}, + {"10", 10}, +} + +local mem = {basiccases} -- for memoization + +local function allcases (n) + if mem[n] then return mem[n] end + local res = {} + -- include all smaller cases + for _, v in ipairs(allcases(n - 1)) do + res[#res + 1] = v + end + for i = 1, n - 1 do + for _, v1 in ipairs(allcases(i)) do + for _, v2 in ipairs(allcases(n - i)) do + res[#res + 1] = { + "(" .. v1[1] .. " and " .. v2[1] .. ")", + v1[2] and v2[2] + } + res[#res + 1] = { + "(" .. v1[1] .. " or " .. v2[1] .. ")", + v1[2] or v2[2] + } + end + end + end + mem[n] = res -- memoize + return res +end + +for _, v in pairs(allcases(4)) do + local res = loadstring("return " .. v[1])() + if res ~= v[2] then + error(string.format("bad conditional eval\n%s\nexpected: %s\ngot: %s", + v[1], tostring(v[2]), tostring(res))) + end +end + +do + -- 0001 KSHORT 1 2 + -- 0002 ISGE 0 1 + -- 0003 JMP 1 => 0006 + -- 0004 KSHORT 1 1 + -- 0005 JMP 1 => 0013 + -- ^^^ must be 2 + -- fix in jmp_patchtestreg + local function fib(n) return (n < 2) and 1 or fib(n-1)+fib(n-2) end + assert(fib(5) == 8) + assert(fib(10) == 89) +end + diff --git a/test/misc/parse_comp.lua b/test/misc/parse_comp.lua new file mode 100644 index 0000000000..5e1948da80 --- /dev/null +++ b/test/misc/parse_comp.lua @@ -0,0 +1,13 @@ + +do + local f = {{n=5}} + local a = f[1].n + assert(1 < a) + assert(1 < (f[1].n)) + assert(1 < f[1].n) +end + +do + tt = { a = 1 } + assert(not(0 >= tt.a)) +end diff --git a/test/misc/parse_esc.lua b/test/misc/parse_esc.lua new file mode 100644 index 0000000000..4bcce0e864 --- /dev/null +++ b/test/misc/parse_esc.lua @@ -0,0 +1,7 @@ +assert("\79\126" == "O~") +assert("\x4f\x7e" == "O~") +assert(loadstring[[return "\xxx"]] == nil) +assert(loadstring[[return "\xxx"]] == nil) +assert(assert(loadstring[[return "abc \z + + def"]])() == "abc def") diff --git a/test/misc/parse_hex.lua b/test/misc/parse_hex.lua new file mode 100644 index 0000000000..bb6da92538 --- /dev/null +++ b/test/misc/parse_hex.lua @@ -0,0 +1,7 @@ +assert(1e5 == 100000) +assert(1e+5 == 100000) +assert(1e-5 == 0.00001) +assert(0xe+9 == 23) +assert(0xep9 == 7168) +assert(0xep+9 == 7168) +assert(0xep-9 == 0.02734375) diff --git a/test/misc/parse_misc.lua b/test/misc/parse_misc.lua new file mode 100644 index 0000000000..8031ec171f --- /dev/null +++ b/test/misc/parse_misc.lua @@ -0,0 +1,31 @@ + +-- Ambiguous syntax: function call vs. new statement. +if os.getenv("LUA52") then + assert(assert(loadstring([[ +local function f() return 99 end +return f +() +]]))() == 99) +else + assert(loadstring([[ +local function f() return 99 end +return f +() +]]) == nil) +end + +-- UTF-8 identifiers. +assert(loadstring([[ +local ä = 1 +local aäa = 2 +local äöü·€晶 = 3 + +assert(ä == 1) +assert(aäa == 2) +assert(äöü·€晶 == 3) + +assert(#"ä" == 2) +assert(#"aäa" == 4) +assert(#"äöü·€晶" == 14) +]]))() + diff --git a/test/misc/pcall_jit.lua b/test/misc/pcall_jit.lua new file mode 100644 index 0000000000..f5dbe62024 --- /dev/null +++ b/test/misc/pcall_jit.lua @@ -0,0 +1,75 @@ + +do + local function f(x) return x*x end + local x = 0 + for i=1,100 do + local ok1, ok2, ok3, y = pcall(pcall, pcall, f, i) + if not ok1 or not ok2 or not ok3 then break end + x = x + y + end + assert(x == 338350) +end + +do + local x = 0 + for i=1,100 do + local ok1, ok2, ok3, y = pcall(pcall, pcall, math.sqrt, i*i) + if not ok1 or not ok2 or not ok3 then break end + x = x + y + end + assert(x == 5050) +end + +do + local function f(x) + if x >= 150 then error("test", 0) end + return x end + local x = 0 + for i=1,200 do + local ok1, ok2, ok3, y = pcall(pcall, pcall, f, i) + if not ok1 or not ok2 or not ok3 then + assert(ok1 and ok2 and not ok3) + assert(y == "test") + break + end + x = x + y + end + assert(x == 11175) +end + +do + local function f(x) + if x >= 150 then return x*x end + return x + end + local x = 0 + for i=1,200 do + local ok1, ok2, ok3, y = pcall(pcall, pcall, f, i) + if not ok1 or not ok2 or not ok3 then break end + x = x + y + end + assert(x == 1584100) +end + +do + local function f(x) + if x >= 150 then + if x >= 175 then error("test", 0) end + return x*x + end + return x + end + local x = 0 + for i=1,200 do + local ok1, ok2, ok3, y = pcall(pcall, pcall, f, i) + if not ok1 or not ok2 or not ok3 then + assert(ok1 and ok2 and not ok3) + assert(y == "test") + -- note: no break, so we get an exit to interpreter + else + x = x + y + end + end + assert(x == 668575) +end + diff --git a/test/misc/phi_conv.lua b/test/misc/phi_conv.lua new file mode 100644 index 0000000000..8d7bea5fdc --- /dev/null +++ b/test/misc/phi_conv.lua @@ -0,0 +1,53 @@ + +local bit = require("bit") + +local Rm = {} +for i=0,16 do Rm[i] = 0 end + +for k=1,10 do + local seed = 1 + for i=16,0,-1 do + seed = bit.band(seed*9069, 0x7fffffff) + Rm[i] = seed + end + assert(seed == 1952688301) +end + +local retindex = 0 +local retdata = { 3, 1, 1, 1, 0, 3, 1, 0, 0, 2, 0, 2, 0, 0, 3, 1, 1, 1, 1 } + +local function get_bits() + retindex = retindex + 1 + return retdata[retindex] +end + +local hufcodes = { [0] = true, [4] = true, [11] = true, [36] = true, [68] = true } + +local maskhuf = { 0x0002, 0x0003, 0x0004, 0x0005, } + +local function decodeCode() + local lookup = get_bits() + local code = hufcodes[lookup] + local z = {1,1,1,1} + if not code then + for i = 1, 4 do + lookup = bit.bor(lookup, bit.lshift(get_bits(), i + 1)) + -- need PHI for CONV num.int of lookup, used in snapshot + code = hufcodes[lookup + maskhuf[i]] + if code then break end + end + end + assert(code) + return code +end + +local function test() + for i = 1, 6 do + decodeCode() + end +end + +if jit and jit.status and jit.status() then jit.opt.start("hotloop=1") end + +test() + diff --git a/test/misc/phi_copyspill.lua b/test/misc/phi_copyspill.lua new file mode 100644 index 0000000000..c62d66fbb4 --- /dev/null +++ b/test/misc/phi_copyspill.lua @@ -0,0 +1,51 @@ +function mat4mul(a11, a21, a31, a41, + a12, a22, a32, a42, + a13, a23, a33, a43, + a14, a24, a34, a44, + b11, b21, b31, b41, + b12, b22, b32, b42, + b13, b23, b33, b43, + b14, b24, b34, b44) + return a11*b11+a21*b12+a31*b13+a41*b14, + a11*b21+a21*b22+a31*b23+a41*b24, + a11*b31+a21*b32+a31*b33+a41*b34, + a11*b41+a21*b42+a31*b43+a41*b44, + a12*b11+a22*b12+a32*b13+a42*b14, + a12*b21+a22*b22+a32*b23+a42*b24, + a12*b31+a22*b32+a32*b33+a42*b34, + a12*b41+a22*b42+a32*b43+a42*b44, + a13*b11+a23*b12+a33*b13+a43*b14, + a13*b21+a23*b22+a33*b23+a43*b24, + a13*b31+a23*b32+a33*b33+a43*b34, + a13*b41+a23*b42+a33*b43+a43*b44, + a14*b11+a24*b12+a34*b13+a44*b14, + a14*b21+a24*b22+a34*b23+a44*b24, + a14*b31+a24*b32+a34*b33+a44*b34, + a14*b41+a24*b42+a34*b43+a44*b44 +end + +local a11, a21, a31, a41 = 1, 0, 0, 0 +local a12, a22, a32, a42 = 0, 1, 0, 0 +local a13, a23, a33, a43 = 0, 0, 1, 0 +local a14, a24, a34, a44 = 0, 0, 0, 1 + +local b11, b21, b31, b41 = 0, 0, -1, 0 +local b12, b22, b32, b42 = 0, 1, 0, 0 +local b13, b23, b33, b43 = 1, 0, 0, 0 +local b14, b24, b34, b44 = 0, 0, 0, 1 + +for i = 1, 1000 do + a11, a21, a31, a41, + a12, a22, a32, a42, + a13, a23, a33, a43, + a14, a24, a34, a44 = mat4mul(a11, a21, a31, a41, + a12, a22, a32, a42, + a13, a23, a33, a43, + a14, a24, a34, a44, + b11, b21, b31, b41, + b12, b22, b32, b42, + b13, b23, b33, b43, + b14, b24, b34, b44) +end +assert(a11 == 1) +assert(a31 == 0) diff --git a/test/misc/phi_ref.lua b/test/misc/phi_ref.lua new file mode 100644 index 0000000000..7493ef466f --- /dev/null +++ b/test/misc/phi_ref.lua @@ -0,0 +1,141 @@ +-- rref points into invariant part +do + local x,y=1,2; for i=1,100 do x=x+y; y=i end + assert(y == 100) +end + +do + local x,y=1,2; for i=1,100.5 do x=x+y; y=i end + assert(y == 100) +end + +do + local x,y=1,2; for i=1,100 do x,y=y,x end + assert(x == 1) + assert(y == 2) +end + +do + local x,y,z=1,2,3; for i=1,100 do x,y,z=y,z,x end + assert(x == 2) + assert(y == 3) + assert(z == 1) +end + +do + local x,y,z=1,2,3; for i=1,100 do x,y,z=z,x,y end + assert(x == 3) + assert(y == 1) + assert(z == 2) +end + +do + local a,x,y,z=0,1,2,3; for i=1,100 do a=a+x; x=y; y=z; z=i end + assert(a == 4759) + assert(x == 98) + assert(y == 99) + assert(z == 100) +end + +-- variant slot, but no corresponding SLOAD +do + local x,y=1,2; for i=1,100 do x=i; y=i-1 end + assert(x == 100) + assert(y == 99) +end + +do + local x,y=1,2; for i=1,100 do x=i; y=i+1 end + assert(x == 100) + assert(y == 101) +end + +do + local x=0; for i=1,100 do if i==90 then break end x=i end + assert(x == 89) +end + +-- dup lref from variant slot (suppressed) +do + local x,y=1,2; for i=1,100 do x=i; y=i end + assert(x == 100) + assert(y == 100) +end + +-- const rref +do + local x,y=1,2 local bxor,tobit=bit.bxor,bit.tobit; + for i=1,100 do x=bxor(i,y); y=tobit(i+1) end + assert(x == 0) + assert(y == 101) +end + +-- dup rref (ok) +do + local x,y,z1,z2=1,2,3,4 local bxor,tobit=bit.bxor,bit.tobit; + for i=1,100 do x=bxor(i,y); z2=tobit(i+5); z1=bxor(x,i+5); y=tobit(i+1) end + assert(x == 0) + assert(y == 101) + assert(z1 == 105) + assert(z2 == 105) +end + +-- variant slot, no corresponding SLOAD, +do + for i=1,5 do + local a, b = 1, 2 + local bits = 0 + while a ~= b do + bits = bits + 1 + a = b + b = bit.lshift(b, 1) + end + assert(bits == 32) + end +end + +-- don't eliminate PHI if referenced from snapshot +do + local t = { 0 } + local a = 0 + for i=1,100 do + local b = t[1] + t[1] = i + a + a = b + end + assert(a == 2500) + assert(t[1] == 2550) +end + +-- don't eliminate PHI if referenced from snapshot +do + local x = 1 + local function f() + local t = {} + for i=1,200 do t[i] = i end + for i=1,200 do + local x1 = x + x = t[i] + if i > 100 then return x1 end + end + end + assert(f() == 100) +end + +-- don't eliminate PHI if referenced from another non-redundant PHI +do + local t = {} + for i=1,256 do + local a, b, k = i, math.floor(i/2), -i + while a > 1 and t[b] > k do + t[a] = t[b] + a = b + b = math.floor(a/2) + end + t[a] = k + end + local x = 0 + for i=1,256 do x = x + bit.bxor(i, t[i]) end + assert(x == -41704) +end + diff --git a/test/misc/phi_rot18.lua b/test/misc/phi_rot18.lua new file mode 100644 index 0000000000..b57b040539 --- /dev/null +++ b/test/misc/phi_rot18.lua @@ -0,0 +1,39 @@ +local function rot18r(N) + local a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 + for x=1,N do + a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r=r,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q + end + return table.concat{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r} +end + +local function rot18l(N) + local a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 + for x=1,N do + a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r=b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,a + end + return table.concat{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r} +end + +assert(rot18r(0) == "123456789101112131415161718") +assert(rot18r(10) == "910111213141516171812345678") +assert(rot18r(105) == "456789101112131415161718123") +assert(rot18r(0) == "123456789101112131415161718") +assert(rot18r(1) == "181234567891011121314151617") +assert(rot18r(2) == "171812345678910111213141516") +assert(rot18r(0) == "123456789101112131415161718") +assert(rot18r(1) == "181234567891011121314151617") +assert(rot18r(2) == "171812345678910111213141516") +assert(rot18r(105) == "456789101112131415161718123") + +assert(rot18l(0) == "123456789101112131415161718") +assert(rot18l(10) == "111213141516171812345678910") +assert(rot18l(105) == "161718123456789101112131415") +assert(rot18l(0) == "123456789101112131415161718") +assert(rot18l(1) == "234567891011121314151617181") +assert(rot18l(2) == "345678910111213141516171812") +assert(rot18l(0) == "123456789101112131415161718") +assert(rot18l(1) == "234567891011121314151617181") +assert(rot18l(2) == "345678910111213141516171812") + +assert(rot18r(100) == "910111213141516171812345678") +assert(rot18l(100) == "111213141516171812345678910") diff --git a/test/misc/phi_rot8.lua b/test/misc/phi_rot8.lua new file mode 100644 index 0000000000..2c249b76da --- /dev/null +++ b/test/misc/phi_rot8.lua @@ -0,0 +1,39 @@ +local function rot8r(n) + local a,b,c,d,e,f,g,h=1,2,3,4,5,6,7,8 + for x=1,n do + a,b,c,d,e,f,g,h=h,a,b,c,d,e,f,g + end + return table.concat{a,b,c,d,e,f,g,h} +end + +local function rot8l(n) + local a,b,c,d,e,f,g,h=1,2,3,4,5,6,7,8 + for x=1,n do + a,b,c,d,e,f,g,h=b,c,d,e,f,g,h,a + end + return table.concat{a,b,c,d,e,f,g,h} +end + +assert(rot8r(0) == "12345678") +assert(rot8r(10) == "78123456") +assert(rot8r(105) == "81234567") +assert(rot8r(0) == "12345678") +assert(rot8r(1) == "81234567") +assert(rot8r(2) == "78123456") +assert(rot8r(0) == "12345678") +assert(rot8r(1) == "81234567") +assert(rot8r(2) == "78123456") +assert(rot8r(105) == "81234567") + +assert(rot8l(0) == "12345678") +assert(rot8l(10) == "34567812") +assert(rot8l(105) == "23456781") +assert(rot8l(0) == "12345678") +assert(rot8l(1) == "23456781") +assert(rot8l(2) == "34567812") +assert(rot8l(0) == "12345678") +assert(rot8l(1) == "23456781") +assert(rot8l(2) == "34567812") + +assert(rot8r(100) == "56781234") +assert(rot8l(100) == "56781234") diff --git a/test/misc/phi_rot9.lua b/test/misc/phi_rot9.lua new file mode 100644 index 0000000000..e7b2821fb7 --- /dev/null +++ b/test/misc/phi_rot9.lua @@ -0,0 +1,39 @@ +local function rot9r(n) + local a,b,c,d,e,f,g,h,i=1,2,3,4,5,6,7,8,9 + for x=1,n do + a,b,c,d,e,f,g,h,i=i,a,b,c,d,e,f,g,h + end + return table.concat{a,b,c,d,e,f,g,h,i} +end + +local function rot9l(n) + local a,b,c,d,e,f,g,h,i=1,2,3,4,5,6,7,8,9 + for x=1,n do + a,b,c,d,e,f,g,h,i=b,c,d,e,f,g,h,i,a + end + return table.concat{a,b,c,d,e,f,g,h,i} +end + +assert(rot9r(0) == "123456789") +assert(rot9r(10) == "912345678") +assert(rot9r(105) == "456789123") +assert(rot9r(0) == "123456789") +assert(rot9r(1) == "912345678") +assert(rot9r(2) == "891234567") +assert(rot9r(0) == "123456789") +assert(rot9r(1) == "912345678") +assert(rot9r(2) == "891234567") +assert(rot9r(105) == "456789123") + +assert(rot9l(0) == "123456789") +assert(rot9l(10) == "234567891") +assert(rot9l(105) == "789123456") +assert(rot9l(0) == "123456789") +assert(rot9l(1) == "234567891") +assert(rot9l(2) == "345678912") +assert(rot9l(0) == "123456789") +assert(rot9l(1) == "234567891") +assert(rot9l(2) == "345678912") + +assert(rot9r(100) == "912345678") +assert(rot9l(100) == "234567891") diff --git a/test/misc/phi_rotx.lua b/test/misc/phi_rotx.lua new file mode 100644 index 0000000000..c8c8b1eb35 --- /dev/null +++ b/test/misc/phi_rotx.lua @@ -0,0 +1,21 @@ +local function rot9r(n, m) + local a,b,c,d,e,f,g,h,i=1,2,3,4,5,6,7,8,9 + local s = "" + for x=1,n do + a,b,c,d,e,f,g,h,i=i,a,b,c,d,e,f,g,h + if x == m then s = table.concat{a,b,c,d,e,f,g,h,i} end + c,d = d,c + end + return table.concat{a,b,c,d,e,f,g,h,i, s} +end + +assert(rot9r(0,0) == "123456789") +assert(rot9r(10,0) == "893124567") +assert(rot9r(105,0) == "913245678") +assert(rot9r(105,90) == "913245678891324567") +assert(rot9r(0,0) == "123456789") +assert(rot9r(1,0) == "913245678") +assert(rot9r(2,0) == "893124567") +assert(rot9r(1,1) == "913245678912345678") +assert(rot9r(2,1) == "893124567912345678") +assert(rot9r(2,2) == "893124567891324567") diff --git a/test/misc/recsum.lua b/test/misc/recsum.lua new file mode 100644 index 0000000000..a25336d1c3 --- /dev/null +++ b/test/misc/recsum.lua @@ -0,0 +1,11 @@ + +do + local function sum(n) + if n == 1 then return 1 end + return n + sum(n-1) + end + for i=1,tonumber(arg and arg[1]) or 100 do + assert(sum(100) == 5050) + end +end + diff --git a/test/misc/recsump.lua b/test/misc/recsump.lua new file mode 100644 index 0000000000..2285018180 --- /dev/null +++ b/test/misc/recsump.lua @@ -0,0 +1,11 @@ + +do + local abs = math.abs + local function sum(n) + if n == 1 then return 1 end + return abs(n + sum(n-1)) + end + for i=1,tonumber(arg and arg[1]) or 100 do + assert(sum(100) == 5050) + end +end diff --git a/test/misc/recurse_deep.lua b/test/misc/recurse_deep.lua new file mode 100644 index 0000000000..9b9af2952f --- /dev/null +++ b/test/misc/recurse_deep.lua @@ -0,0 +1,29 @@ + +do + local function sum(n) + if n == 1 then return 1 end + return n + sum(n-1) + end + assert(sum(200) == 20100) +end + +do + local pcall = pcall + local tr1 + local x = 0 + function tr1(n) + if n <= 0 then return end + x = x + 1 + return pcall(tr1, n-1) + end + assert(tr1(200) == true and x == 200) +end + +do + local function fib(n) + if n < 2 then return 1 end + return fib(n-2) + fib(n-1) + end + assert(fib(15) == 987) +end + diff --git a/test/misc/recurse_tail.lua b/test/misc/recurse_tail.lua new file mode 100644 index 0000000000..ef76443211 --- /dev/null +++ b/test/misc/recurse_tail.lua @@ -0,0 +1,22 @@ + +do + local tr1 + function tr1(n) + if n <= 0 then return 0 end + return tr1(n-1) + end + assert(tr1(200) == 0) +end + +do + local tr1, tr2 + function tr1(n) + if n <= 0 then return 0 end + return tr2(n-1) + end + function tr2(n) + return tr1(n) + end + assert(tr2(200) == 0) +end + diff --git a/test/misc/select.lua b/test/misc/select.lua new file mode 100644 index 0000000000..4d9cda853b --- /dev/null +++ b/test/misc/select.lua @@ -0,0 +1,88 @@ + +do + local x = 0 + for i=1,100 do + x = x + select("#", 3, 4) + end + assert(x == 200) +end + +do + local x = 0 + for i=1,100 do + x = x + select("#", math.modf(i)) + end + assert(x == 200) +end + +do + local x = 0 + for i=1,100 do + x = x + select(1, i) + end + assert(x == 5050) +end + +do + local x, y = 0, 0 + for i=1,100 do + local a, b = select(2, 1, i, i+10) + x = x + a + y = y + b + end + assert(x == 5050 and y == 6050) +end + +do + local function f(a, ...) + local x = 0 + for i=1,select('#', ...) do + x = x + select(i, ...) + end + assert(x == a) + end + for i=1,100 do + f(1, 1) + f(3, 1, 2) + f(15, 1, 2, 3, 4, 5) + f(0) + f(3200, string.byte(string.rep(" ", 100), 1, 100)) + end +end + +do + local function f(a, ...) + local x = 0 + for i=1,20 do + local b = select(i, ...) + if b then x = x + b else x = x + 9 end + end + assert(x == a) + end + for i=1,100 do + f(172, 1) + f(165, 1, 2) + f(150, 1, 2, 3, 4, 5) + f(180) + f(640, string.byte(string.rep(" ", 100), 1, 100)) + end +end + +do + local function f(a, ...) + local x = 0 + for i=1,20 do + local b = select(4, ...) + if b then x = x + b else x = x + 9 end + end + assert(x == a) + end + for i=1,100 do + f(180, 1) + f(180, 1, 2) + f(80, 1, 2, 3, 4, 5) + f(180) + f(640, string.byte(string.rep(" ", 100), 1, 100)) + end +end + diff --git a/test/misc/self.lua b/test/misc/self.lua new file mode 100644 index 0000000000..afb95ce041 --- /dev/null +++ b/test/misc/self.lua @@ -0,0 +1,18 @@ +local t={} + +function t:set(x) + self.a=x +end + +function t:get() + return self.a +end + +t:set("foo") +assert(t:get() == "foo") +assert(t.a == "foo") + +t:set(42) +assert(t:get() == 42) +assert(t.a == 42) + diff --git a/test/misc/sink_alloc.lua b/test/misc/sink_alloc.lua new file mode 100644 index 0000000000..55f4218e63 --- /dev/null +++ b/test/misc/sink_alloc.lua @@ -0,0 +1,140 @@ + +local assert = assert + +-- DCE or sink trivial TNEW or TDUP. +do + for i=1,100 do local t={} end + for i=1,100 do local t={1} end +end + +-- Sink TNEW/TDUP + ASTORE/HSTORE. +do + for i=1,100 do local t={i}; assert(t[1] == i) end + for i=1,100 do local t={foo=i}; assert(t.foo == i) end + for i=1,100 do local t={1,i}; assert(t[2] == i) end + for i=1,100 do local t={bar=1,foo=i}; assert(t.foo == i) end +end + +-- Sink outermost table of nested TNEW. +do + local x + for i=1,100 do + local t = {[0]={{1,i}}} + if i == 90 then x = t end + assert(t[0][1][2] == i) + end + assert(x[0][1][2] == 90) + for i=1,100 do + local t = {foo={bar={baz=i}}} + if i == 90 then x = t end + assert(t.foo.bar.baz == i) + end + assert(x.foo.bar.baz == 90) +end + +-- Sink one TNEW + FSTORE. +do + for i=1,100 do local t = setmetatable({}, {}) end +end + +-- Sink TDUP or TDUP + HSTORE. Guard of HREFK eliminated. +do + local x + for i=1,100 do local t = { foo = 1 }; x = t.foo; end + assert(x == 1) + for i=1,100 do local t = { foo = i }; x = t.foo; end + assert(x == 100) +end + +-- Sink of simplified complex add, unused in next iteration, drop PHI. +do + local x={1,2} + for i=1,100 do x = {x[1]+3, x[2]+4} end + assert(x[1] == 301) + assert(x[2] == 402) +end + +-- Sink of complex add, unused in next iteration, drop PHI. +do + local x,k={1.5,2.5},{3.5,4.5} + for i=1,100 do x = {x[1]+k[1], x[2]+k[2]} end + assert(x[1] == 351.5) + assert(x[2] == 452.5) +end + +-- Sink of TDUP with stored values that are both PHI and non-PHI. +do + local x,k={1,2},{3,4} + for i=1,100 do x = {x[1]+k[1], k[2]} end + assert(x[1] == 301) + assert(x[2] == 4) +end + +-- Sink of CONV. +do + local t = {1} + local x,y + for i=1,200 do + local v = {i} + local w = {i+1} + x = v[1] + y = w[1] + if i > 100 then end + end + assert(x == 200 and y == 201) +end + +-- Sink of stores with numbers. +do + local x = {1.5, 0} + for i=1,200 do x = {x[1]+1, 99.5}; x[2]=4.5; if i > 100 then end end + assert(x[1] == 201.5) + assert(x[2] == 4.5) +end + +-- Sink of stores with constants. +do + for i=1,100 do local t = {false}; t[1] = true; if i > 100 then g=t end end +end + +-- Sink with two references to the same table. +do + for i=1,200 do + local t = {i} + local q = t + if i > 100 then assert(t == q) end + end +end + +do + local point + point = { + new = function(self, x, y) + return setmetatable({x=x, y=y}, self) + end, + __add = function(a, b) + return point:new(a.x + b.x, a.y + b.y) + end, + } + point.__index = point + local a, b = point:new(1, 1), point:new(2, 2) + for i=1,100 do a = (a + b) + b end + assert(a.x == 401) + assert(a.y == 401) + assert(getmetatable(a) == point) + for i=1,200 do a = (a + b) + b; if i > 100 then end end + assert(a.x == 1201) + assert(a.y == 1201) + assert(getmetatable(a) == point) +end + +do + local t = {} + for i=1,20 do t[i] = 1 end + for i=1,20 do + for a,b in ipairs(t) do + local s = {i} + end + end +end + diff --git a/test/misc/sink_nosink.lua b/test/misc/sink_nosink.lua new file mode 100644 index 0000000000..5badfd2690 --- /dev/null +++ b/test/misc/sink_nosink.lua @@ -0,0 +1,124 @@ + +local assert = assert + +-- Cannot sink TNEW, aliased load. +do + local k = 1 + for i=1,100 do local t={i}; assert(t[k]==i) end + for i=1,100 do local t={}; t[k]=i; assert(t[1]==i) end +end + +-- Cannot sink TNEW, escaping to upvalue. +do + (function() + local uv + return function() + for i=1,100 do uv = {i} end + assert(uv[1] == 100) + end + end)()() +end + +-- Cannot sink TNEW, escaping through a store. +do + local t = {} + for i=1,100 do t[1] = {i} end + for i=1,100 do t.foo = {i} end + for i=1,100 do setmetatable(t, {i}) end + assert(t[1][1] == 100) + assert(t.foo[1] == 100) + assert(getmetatable(t)[1] == 100) +end + +-- Cannot sink TNEW, iteratively escaping through a store. +do + local t = {} + for i=1,100 do t[1] = {i}; t[1][1] = {i} end + assert(t[1][1][1] == 100) +end + +-- Cannot sink TNEW, escaping to next iteration (unused in 1st variant). +do + local t; + for i=1,200 do t = {i} end + assert(t[1] == 200) + for i=1,200 do if i > 100 then assert(t[1] == i-1) end t = {i} end + assert(t[1] == 200) +end + +-- Cannot sink TNEW, escaping to next iteration (snapshot ref). +do + local t,x + for i=1,100 do x=t; t={i} end + assert(t[1] == 100) + assert(x[1] == 99) +end + +-- Cannot sink TNEW, escaping to next iteration (IR/snapshot ref). +do + local t + for i=1,100 do t={t} end + assert(type(t[1][1][1]) == "table") +end + +-- Could sink outer TNEW, but the logic for stores to PHI allocs is too simple. +-- Cannot sink inner TNEW, escaping to next iteration (IR ref). +do + local t = {42, 43} + for i=1,100 do t={t[2], {i}} end + assert(t[2][1] == 100) + assert(t[1][1] == 99) +end + +-- Cannot sink TNEW, cross-PHI ref (and snapshot ref). +do + local x,y + for i=1,100 do x,y={i},x end + assert(x[1] == 100) + assert(y[1] == 99) +end + +-- Cannot sink TNEW, cross-PHI ref (and snapshot ref). +do + local x,y + for i=1,100 do x,y=y,{i} end + assert(x[1] == 99) + assert(y[1] == 100) +end + +-- Cannot sink TNEW, escaping to exit. +do + local function f(n, t) + if n == 0 then return t end + return (f(n-1, {t})) + end + local t = f(100, 42) + assert(type(t[1][1][1]) == "table") + t = f(3, 42) + assert(t[1][1][1] == 42) +end + +-- Cannot sink TNEW, escaping to exit. +do + local function f(n) + if n == 0 then return 42 end + local t = f(n-1) + return {t} + end + for i=1,20 do + local t = f(100) + assert(type(t[1][1][1]) == "table") + end + t = f(3) + assert(t[1][1][1] == 42) +end + +-- Cannot sink, since nested inner table is non-PHI. +do + local a, b = {{1}}, {{1}} + for i=1,10000 do -- Need to force GC exit sometimes + a = {{a[1][1]+b[1][1]}} + end + assert(a[1][1] == 10001) +end + diff --git a/test/misc/snap_gcexit.lua b/test/misc/snap_gcexit.lua new file mode 100644 index 0000000000..37fce65ae4 --- /dev/null +++ b/test/misc/snap_gcexit.lua @@ -0,0 +1,16 @@ + +do + local x = 0 + local t + for i=1,1000 do + if i >= 100 then + -- causes an exit for atomic phase + -- must not merge snapshot #0 with comparison since it has the wrong PC + if i < 150 then x=x+1 end + t = {i} + end + end + assert(x == 50) + assert(t[1] == 1000) +end + diff --git a/test/misc/snap_top.lua b/test/misc/snap_top.lua new file mode 100644 index 0000000000..a717a15989 --- /dev/null +++ b/test/misc/snap_top.lua @@ -0,0 +1,16 @@ + +do + function randomtable(entries, depth) + if depth == 0 then + return tostring(math.random(2)) -- snapshot between return and CALLMT + end + local t = {} + for k=1,entries do + t[k] = randomtable(entries, depth-1) + end + return t + end + + local t = randomtable(10, 2) +end + diff --git a/test/misc/snap_top2.lua b/test/misc/snap_top2.lua new file mode 100644 index 0000000000..4504dc3e23 --- /dev/null +++ b/test/misc/snap_top2.lua @@ -0,0 +1,15 @@ + +local function f() + gcinfo() + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ +end + +for i=1,100 do + f() + if i % 3 == 0 then collectgarbage() end +end diff --git a/test/misc/sort.lua b/test/misc/sort.lua new file mode 100644 index 0000000000..4b2e40129e --- /dev/null +++ b/test/misc/sort.lua @@ -0,0 +1,23 @@ +-- Really a test for lua_lessthan() + +local N = 1000 + +math.randomseed(42) +local t = {} +for i=1,N do t[i] = math.random(N) end +table.sort(t) +for i=2,N do assert(t[i-1] <= t[i]) end + +math.randomseed(42) +local t = {} +for i=1,N do t[i] = math.random(N).."" end +table.sort(t) +for i=2,N do assert(t[i-1] <= t[i]) end + +math.randomseed(42) +local mt = { __lt = function(a,b) return a[1] < b[1] end } +local t = {} +for i=1,N do t[i] = setmetatable({ math.random(N) }, mt) end +table.sort(t) +for i=2,N do assert(t[i-1][1] <= t[i][1]) end + diff --git a/test/misc/stack_gc.lua b/test/misc/stack_gc.lua new file mode 100644 index 0000000000..656a06a054 --- /dev/null +++ b/test/misc/stack_gc.lua @@ -0,0 +1,15 @@ + +do + local t = setmetatable({}, { __index=function(t, k) + k = k - 1 + if k == 0 then + collectgarbage() -- Mark stack, including holes. + return 0 + else + return t[k] -- Leaves holes in each frame. + end + do local a,b,c,d,e,f,g,h,i,j,k,l,m,n end -- Ensure bigger frame size. + end}) + local x = t[50] +end + diff --git a/test/misc/stack_purge.lua b/test/misc/stack_purge.lua new file mode 100644 index 0000000000..bfaee0f3e9 --- /dev/null +++ b/test/misc/stack_purge.lua @@ -0,0 +1,25 @@ + +-- Must preserve the modified function slot in the RET snapshot. +local function a() + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + local _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ + return 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +end + +local function b() + return a() +end + +local function c() + for j=1,10 do + for i=1,50 do b() b() b() end + collectgarbage() + local t = {} + for i=1,50 do t = {t} end + end +end + +jit.off(c) +c() + diff --git a/test/misc/stackov.lua b/test/misc/stackov.lua new file mode 100644 index 0000000000..ef105af6f9 --- /dev/null +++ b/test/misc/stackov.lua @@ -0,0 +1,40 @@ + +local function f() + f() +end + +local err, s = xpcall(f, debug.traceback) +assert(err == false) + +local first = string.match(s, "[^\n]+") +local line = debug.getinfo(f, "S").linedefined+1 +assert(string.match(first, ":"..line..": stack overflow$")) + +local n = 1 +for _ in string.gmatch(s, "\n") do n = n + 1 end +assert(n == 1+1+11+1+10) + +local function g(i) + g(i) +end + +local err, s = xpcall(g, debug.traceback, 1) +assert(err == false) + +--[[ +-- too slow +local function vtail(...) + return vtail(1, ...) +end + +local err, s = xpcall(vtail, debug.traceback, 1) +assert(err == false) +--]] + +local function vcall(...) + vcall(1, ...) +end + +local err, s = xpcall(vcall, debug.traceback, 1) +assert(err == false) + diff --git a/test/misc/stackovc.lua b/test/misc/stackovc.lua new file mode 100644 index 0000000000..c00bcbd825 --- /dev/null +++ b/test/misc/stackovc.lua @@ -0,0 +1,4 @@ +local j = 1e4 +local co = coroutine.create(function() t = {} for i = 1, j do t[i] = i end return unpack(t) end) +local ok, err = coroutine.resume(co) +assert(not ok and string.find(err, "unpack")) diff --git a/test/misc/stitch.lua b/test/misc/stitch.lua new file mode 100644 index 0000000000..1eba1810bb --- /dev/null +++ b/test/misc/stitch.lua @@ -0,0 +1,21 @@ + +do + local tonumber = tonumber + local function octal(s) return tonumber(s, 8) end + for i=1,100 do + octal("1") + octal("1") + octal("1") + end +end + +do + local t = { + [0] = function() end, + coroutine.wrap(function() while true do coroutine.yield() end end), + } + for i=1,100 do + t[i % 2]() + end +end + diff --git a/test/misc/strcmp.lua b/test/misc/strcmp.lua new file mode 100644 index 0000000000..53d1ce5f0e --- /dev/null +++ b/test/misc/strcmp.lua @@ -0,0 +1,11 @@ + +do + local a = "\255\255\255\255" + local b = "\1\1\1\1" + + assert(a > b) + assert(a > b) + assert(a >= b) + assert(b <= a) +end + diff --git a/test/misc/string_byte.lua b/test/misc/string_byte.lua new file mode 100644 index 0000000000..cd565ecb48 --- /dev/null +++ b/test/misc/string_byte.lua @@ -0,0 +1,90 @@ + +local band, bor = bit.band, bit.bor +local byte = string.byte + +-- Fixed slice [i,i+k] or overflow +do + local s = "abcdefg" + local x,y,z + for j=100,107 do + for i=1,j do x,y,z = byte("abcdefg", band(i, 7), band(i+2, 7)) end + local a,b,c = byte("abcdefg", band(j, 7), band(j+2, 7)) + assert(x == a and y == b and z == c) + end + for j=100,107 do + for i=1,j do x,y,z = byte(s, band(i, 7), band(i+2, 7)) end + local a,b,c = byte(s, band(j, 7), band(j+2, 7)) + assert(x == a and y == b and z == c) + end +end + +-- Positive slice [i,len] or overflow +do + local s = "abc" + local x,y,z + for j=100,107 do + for i=1,j do x,y,z = byte("abc", band(i, 7), -1) end + local a,b,c = byte("abc", band(j, 7), -1) + assert(x == a and y == b and z == c) + end + for j=100,107 do + for i=1,j do x,y,z = byte(s, band(i, 7), -1) end + local a,b,c = byte(s, band(j, 7), -1) + assert(x == a and y == b and z == c) + end +end + +-- Negative slice [-i,len] or underflow +do + local s = "abc" + local x,y,z + for j=-100,-107,-1 do + for i=-1,j,-1 do x,y,z = byte("abc", bor(i, -8), -1) end + local a,b,c = byte("abc", bor(j, -8), -1) + assert(x == a and y == b and z == c) + end + for j=-100,-107,-1 do + for i=-1,j,-1 do x,y,z = byte(s, bor(i, -8), -1) end + local a,b,c = byte(s, bor(j, -8), -1) + assert(x == a and y == b and z == c) + end +end + +-- Positive slice [1,i] or overflow +do + local s = "abc" + local x,y,z + for j=100,107 do + for i=1,j do x,y,z = byte("abc", 1, band(i, 7)) end + local a,b,c = byte("abc", 1, band(j, 7)) + assert(x == a and y == b and z == c) + end + for j=100,107 do + for i=1,j do x,y,z = byte(s, 1, band(i, 7)) end + local a,b,c = byte(s, 1, band(j, 7)) + assert(x == a and y == b and z == c) + end +end + +-- Negative slice [1,-i] or underflow +do + local s = "abc" + local x,y,z + for j=-100,-107,-1 do + for i=-1,j,-1 do x,y,z = byte("abc", 1, bor(i, -8)) end + local a,b,c = byte("abc", 1, bor(j, -8)) + assert(x == a and y == b and z == c) + end + for j=-100,-107,-1 do + for i=-1,j,-1 do x,y,z = byte(s, 1, bor(i, -8)) end + local a,b,c = byte(s, 1, bor(j, -8)) + assert(x == a and y == b and z == c) + end +end + +-- Check for slot stack overflow +do + local s = string.rep("x", 500) + for i=1,100 do byte(s, 1, 500) end +end + diff --git a/test/misc/string_char.lua b/test/misc/string_char.lua new file mode 100644 index 0000000000..3920c3a4c2 --- /dev/null +++ b/test/misc/string_char.lua @@ -0,0 +1,28 @@ + +do + local y + for i=1,100 do y = string.char(65) end + assert(y == "A") + local x = 97 + for i=1,100 do y = string.char(x) end + assert(y == "a") + x = "98" + for i=1,100 do y = string.char(x) end + assert(y == "b") + for i=1,100 do y = string.char(32+i) end + assert(y == "\132") +end + +do + local y + assert(not pcall(function() + for i=1,200 do y = string.char(100+i) end + end)) + assert(y == "\255") +end + +do + local y + for i=1,100 do y = string.char(65, 66, i, 67, 68) end + assert(y == "ABdCD") +end diff --git a/test/misc/string_dump.lua b/test/misc/string_dump.lua new file mode 100644 index 0000000000..a255b8a895 --- /dev/null +++ b/test/misc/string_dump.lua @@ -0,0 +1,31 @@ + +-- Must unpatch modified bytecode with ILOOP/JLOOP etc. +do + local function foo() + local t = {} + for i=1,100 do t[i] = i end + for a,b in ipairs(t) do end + local m = 0 + while m < 100 do m = m + 1 end + end + + local d1 = string.dump(foo) + foo() + assert(string.dump(foo) == d1) + jit.off(foo) + foo() + assert(string.dump(foo) == d1) + local d2 = string.dump(loadstring(d1, ""), true) + local d3 = string.dump(assert(loadstring(d2, "")), true) + assert(d2 == d3) + assert(loadstring(string.dump(assert(loadstring(d2, ""))))) +end + +do + local function f1() return -0x80000000 end + local function f2() return 0.971234567 end + assert(f1() == -0x80000000) + assert(loadstring(string.dump(f1), "")() == -0x80000000) + assert(f2() == 0.971234567) + assert(loadstring(string.dump(f2), "")() == 0.971234567) +end diff --git a/test/misc/string_op.lua b/test/misc/string_op.lua new file mode 100644 index 0000000000..6adb790ee2 --- /dev/null +++ b/test/misc/string_op.lua @@ -0,0 +1,110 @@ + +do + local y + for i=1,100 do y = string.reverse("abc") end + assert(y == "cba") + local x = "abcd" + for i=1,100 do y = string.reverse(x) end + assert(y == "dcba") + x = 1234 + for i=1,100 do y = string.reverse(x) end + assert(y == "4321") +end + +do + local y + for i=1,100 do y = string.upper("aBc9") end + assert(y == "ABC9") + local x = ":abCd+" + for i=1,100 do y = string.upper(x) end + assert(y == ":ABCD+") + x = 1234 + for i=1,100 do y = string.upper(x) end + assert(y == "1234") +end + +do + local y + for i=1,100 do y = string.lower("aBc9") end + assert(y == "abc9") + local x = ":abcd+" + for i=1,100 do y = string.lower(x) end + assert(y == ":abcd+") + x = 1234 + for i=1,100 do y = string.lower(x) end + assert(y == "1234") +end + +do + local t, y = {}, {} + for i=1,100 do t[i] = string.char(i, 16+i, 32+i) end + for i=1,100 do t[i] = string.reverse(t[i]) end + assert(t[100] == "\132\116\100") + for i=1,100 do t[i] = string.reverse(t[i]) end + for i=1,100 do assert(t[i] == string.char(i, 16+i, 32+i)) end + for i=1,100 do y[i] = string.upper(t[i]) end + assert(y[65] == "AQA") + assert(y[97] == "AQ\129") + assert(y[100] == "DT\132") + for i=1,100 do y[i] = string.lower(t[i]) end + assert(y[65] == "aqa") + assert(y[97] == "aq\129") + assert(y[100] == "dt\132") +end + +do + local y, z + local x = "aBcDe" + for i=1,100 do + y = string.upper(x) + z = y.."fgh" + end + assert(y == "ABCDE") + assert(z == "ABCDEfgh") +end + +do + local y + for i=1,100 do y = string.rep("a", 10) end + assert(y == "aaaaaaaaaa") + for i=1,100 do y = string.rep("ab", 10) end + assert(y == "abababababababababab") + for i=1,100 do y = string.rep("ab", 10, "c") end + assert(y == "abcabcabcabcabcabcabcabcabcab") + local x = "a" + for i=1,100 do y = string.rep(x, 10) end + assert(y == "aaaaaaaaaa") + local n = 10 + for i=1,100 do y = string.rep(x, n) end + assert(y == "aaaaaaaaaa") + x = "ab" + for i=1,100 do y = string.rep(x, n) end + assert(y == "abababababababababab") + x = 12 + n = "10" + for i=1,100 do y = string.rep(x, n) end + assert(y == "12121212121212121212") +end + +do + local t = {} + for i=1,100 do t[i] = string.rep("ab", i-85) end + assert(t[100] == "ababababababababababababababab") + for i=1,100 do t[i] = string.rep("ab", i-85, "c") end + assert(t[85] == "") + assert(t[86] == "ab") + assert(t[87] == "abcab") + assert(t[100] == "abcabcabcabcabcabcabcabcabcabcabcabcabcabcab") +end + +do + local y, z + local x = "ab" + for i=1,100 do + y = string.rep(x, i-90) + z = y.."fgh" + end + assert(y == "abababababababababab") + assert(z == "ababababababababababfgh") +end + diff --git a/test/misc/string_sub.lua b/test/misc/string_sub.lua new file mode 100644 index 0000000000..2cdfac766a --- /dev/null +++ b/test/misc/string_sub.lua @@ -0,0 +1,60 @@ + +local band, bor = bit.band, bit.bor +local sub = string.sub + +-- Positive slice [i,len] or overflow +do + local s = "abc" + local x + for j=100,107 do + for i=1,j do x = sub("abc", band(i, 7)) end + assert(x == sub("abc", band(j, 7))) + end + for j=100,107 do + for i=1,j do x = sub(s, band(i, 7)) end + assert(x == sub(s, band(j, 7))) + end +end + +-- Negative slice [-i,len] or underflow +do + local s = "abc" + local x + for j=-100,-107,-1 do + for i=-1,j,-1 do x = sub("abc", bor(i, -8)) end + assert(x == sub("abc", bor(j, -8))) + end + for j=-100,-107,-1 do + for i=-1,j,-1 do x = sub(s, bor(i, -8)) end + assert(x == sub(s, bor(j, -8))) + end +end + +-- Positive slice [1,i] or overflow +do + local s = "abc" + local x + for j=100,107 do + for i=1,j do x = sub("abc", 1, band(i, 7)) end + assert(x == sub("abc", 1, band(j, 7))) + end + for j=100,107 do + for i=1,j do x = sub(s, 1, band(i, 7)) end + assert(x == sub(s, 1, band(j, 7))) + end +end + +-- Negative slice [1,-i] or underflow +do + local s = "abc" + local x + for j=-100,-107,-1 do + for i=-1,j,-1 do x = sub("abc", 1, bor(i, -8)) end + assert(x == sub("abc", 1, bor(j, -8))) + end + for j=-100,-107,-1 do + for i=-1,j,-1 do x = sub(s, 1, bor(i, -8)) end + assert(x == sub(s, 1, bor(j, -8))) + end +end + diff --git a/test/misc/string_sub_opt.lua b/test/misc/string_sub_opt.lua new file mode 100644 index 0000000000..235767aff8 --- /dev/null +++ b/test/misc/string_sub_opt.lua @@ -0,0 +1,109 @@ + +do + local s = "abcde" + local x = 0 + for i=1,100 do + if string.sub(s, 1, 1) == "a" then x = x + 1 end + end + assert(x == 100) +end + +do + local s = "abcde" + local x = 0 + for i=1,100 do + if string.sub(s, 1, 1) == "b" then x = x + 1 end + end + assert(x == 0) +end + +do + local s = "abcde" + local x = 0 + for i=1,100 do + if string.sub(s, 1, 1) == "ab" then x = x + 1 end + end + assert(x == 0) +end + +do + local s = "abcde" + local x = 0 + for i=1,100 do + if string.sub(s, 1, 2) == "a" then x = x + 1 end + end + assert(x == 0) +end + +do + local s = "abcde" + local x = 0 + local k = 1 + for i=1,100 do + if string.sub(s, 1, k) == "a" then x = x + 1 end + end + assert(x == 100) +end + +do + local s = "abcde" + local x = 0 + local k = 1 + for i=1,100 do + if string.sub(s, 1, k) == "b" then x = x + 1 end + end + assert(x == 0) +end + +do + local s = "abcde" + local x = 0 + local k = 1 + for i=1,100 do + if string.sub(s, 1, k) == "ab" then x = x + 1 end + end + assert(x == 0) +end + +---- + +do + local s = "abcde" + local x = 0 + for i=1,100 do + if string.sub(s, 1, 2) == "ab" then x = x + 1 end + end + assert(x == 100) +end + +do + local s = "abcde" + local x = 0 + for i=1,100 do + if string.sub(s, 1, 3) == "abc" then x = x + 1 end + end + assert(x == 100) +end + +do + local s = "abcde" + local x = 0 + for i=1,100 do + if string.sub(s, 1, 4) == "abcd" then x = x + 1 end + end + assert(x == 100) +end + +do + local t = {} + local line = string.rep("..XX", 100) + local i = 1 + local c = line:sub(i, i) + while c ~= "" and c ~= "Z" do + t[i] = c == "X" and "Y" or c + i = i + 1 + c = line:sub(i, i) + end + assert(table.concat(t) == string.rep("..YY", 100)) +end + diff --git a/test/misc/table_insert.lua b/test/misc/table_insert.lua new file mode 100644 index 0000000000..28a094dae9 --- /dev/null +++ b/test/misc/table_insert.lua @@ -0,0 +1,18 @@ + +local tinsert = table.insert +local assert = assert + +do + local t = {} + for i=1,100 do t[i] = i end + for i=1,100 do tinsert(t, i) end + assert(#t == 200 and t[100] == 100 and t[200] == 100) +end + +do + local t = {} + for i=1,200 do t[i] = i end + for i=101,200 do tinsert(t, i, i) end + assert(#t == 300 and t[101] == 101 and t[200] == 200 and t[300] == 200) +end + diff --git a/test/misc/table_misc.lua b/test/misc/table_misc.lua new file mode 100644 index 0000000000..9362b254ea --- /dev/null +++ b/test/misc/table_misc.lua @@ -0,0 +1,131 @@ + +-- ABC elim +do + local s, t = {}, {} + for i=1,100 do t[i] = 1 end + for i=1,100 do s[i] = t end + s[90] = {} + local n = 100 + for i=1,n do s[i][i] = i end +end + +-- TSETM +do + local function f(a,b,c) + return a,b,c + end + + local t + + t = {(f(1,2,3))} + assert(t[1] == 1 and t[2] == nil and t[3] == nil) + + t = {f(1,2,3)} + assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == nil) + t = {f(1,2,3),} + assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == nil) + + t = {f(1,2,3), f(4,5,6)} + assert(t[1] == 1 and t[2] == 4 and t[3] == 5 and t[4] == 6 and t[5] == nil) + + t = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + f(2,3,4)} + assert(t[255] == 1 and t[256] == 2 and t[257] == 3 and t[258] == 4 and t[259] == nil) +end + +do + local function f() return 9, 10 end + for i=1,100 do t = { 1, 2, 3, f() } end + assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 9 and t[5] == 10 and + t[6] == nil) +end + +-- table.new +do + local tnew = require("table.new") + local x, y + for i=1,100 do + x = tnew(100, 30) + if i == 90 then y = x end + end + assert(x ~= y) +end + +-- table.concat +do + local t = {a=1,b=2,c=3,d=4,e=5} + t[1] = 4 + t[3] = 6 + local ok, err = pcall(table.concat, t, "", 1, 3) + assert(not ok and err:match("index 2 ")) + local q = {} + for i=1,100 do q[i] = {9,8,7} end + q[90] = t + for i=1,100 do + assert(pcall(table.concat, q[i], "", 1, 3) == (i ~= 90)) + end + t[2] = 5 -- index 1 - 3 in hash part + q[91] = {} + q[92] = {9} + for i=1,100 do q[i] = table.concat(q[i], "x") end + assert(q[90] == "4x5x6") + assert(q[91] == "") + assert(q[92] == "9") + assert(q[93] == "9x8x7") +end + +-- table.concat must inhibit CSE and DSE +do + local t = {1,2,3} + local y, z + for i=1,100 do + y = table.concat(t, "x", 1, 3) + t[2] = i + z = table.concat(t, "x", 1, 3) + end + assert(y == "1x99x3") + assert(z == "1x100x3") +end + +do + local y + for i=1,100 do + local t = {1,2,3} + t[2] = 4 + y = table.concat(t, "x") + t[2] = 9 + end + assert(y == "1x4x3") +end + +do + local t = {[0]={}, {}, {}, {}} + for i=1,30 do + for j=3,0,-1 do + t[j].x = t[j-1] + end + end +end + +-- table.pack +if os.getenv("LUA52") then + local t + + t = table.pack() + assert(t.n == 0 and t[0] == nil and t[1] == nil) + + t = table.pack(99) + assert(t.n == 1 and t[0] == nil and t[1] == 99 and t[2] == nil) + + t = table.pack(nil, nil, nil) + assert(t.n == 3 and t[0] == nil and t[1] == nil and t[2] == nil and t[3] == nil and t[4] == nil) +end + diff --git a/test/misc/table_remove.lua b/test/misc/table_remove.lua new file mode 100644 index 0000000000..d8cdd19dcb --- /dev/null +++ b/test/misc/table_remove.lua @@ -0,0 +1,25 @@ + +local tremove = table.remove +local assert = assert + +do + local t = {} + for i=1,200 do t[i] = i end + for i=1,100 do tremove(t) end + assert(#t == 100 and t[100] == 100) +end + +do + local t = {} + for i=1,200 do t[i] = i end + for i=1,100 do assert(tremove(t) == 201-i) end + assert(#t == 100 and t[100] == 100) +end + +do + local t = {} + for i=1,200 do t[i] = i end + for i=1,100 do assert(tremove(t, 1) == i) end + assert(#t == 100 and t[100] == 200) +end + diff --git a/test/misc/tak.lua b/test/misc/tak.lua new file mode 100644 index 0000000000..a67478350c --- /dev/null +++ b/test/misc/tak.lua @@ -0,0 +1,12 @@ + +local function tak(x, y, z) + if y >= x then return z end + return tak(tak(x-1, y, z), tak(y-1, z, x), (tak(z-1, x, y))) +end + +if arg and arg[1] then + local N = tonumber(arg and arg[1]) or 7 + print(tak(3*N, 2*N, N)) +else + assert(tak(21, 14, 7) == 14) +end diff --git a/test/misc/tcall_base.lua b/test/misc/tcall_base.lua new file mode 100644 index 0000000000..c6c4ae1a31 --- /dev/null +++ b/test/misc/tcall_base.lua @@ -0,0 +1,20 @@ + +local r = 0 +local function g() + r = r + 1 + for i=1,100 do end +end + +local function f() + for j=1,20 do + if j > 19 then + return g() -- Tailcall at base. + -- Let this link to the already compiled loop in g(). + end + end +end + +g() -- Compile this loop first. +for i=1,50 do f() end +assert(r == 51) + diff --git a/test/misc/tcall_loop.lua b/test/misc/tcall_loop.lua new file mode 100644 index 0000000000..d3c6f1a6de --- /dev/null +++ b/test/misc/tcall_loop.lua @@ -0,0 +1,8 @@ +local function f(i) + if i > 0 then return f(i-1) end + return 1 +end + +local x = 0 +for i=1,100 do x = x + f(1000) end +assert(x == 100) diff --git a/test/misc/tlen_loop.lua b/test/misc/tlen_loop.lua new file mode 100644 index 0000000000..050f23c389 --- /dev/null +++ b/test/misc/tlen_loop.lua @@ -0,0 +1,23 @@ + +do + local t = {} + for i=1,100 do t[#t+1] = i end + assert(#t == 100) + for i=1,100 do t[#t] = nil end +end + +do + local t = {} + t[90] = 999 + for i=1,100 do t[#t+1] = i end + assert(#t > 100 and t[#t] == 100) +end + +do + local t = {} + for i=1,100 do t[i] = i end + t[10] = nil + for i=1,99 do t[#t] = nil end + assert(#t == 0) +end + diff --git a/test/misc/tnew_tdup.lua b/test/misc/tnew_tdup.lua new file mode 100644 index 0000000000..140d05dee3 --- /dev/null +++ b/test/misc/tnew_tdup.lua @@ -0,0 +1,16 @@ + +do + local a = nil + local b = {} + local t = {[true] = a, [false] = b or 1} + assert(t[true] == nil) + assert(t[false] == b) +end + +do + local b = {} + local t = {[true] = nil, [false] = b or 1} + assert(t[true] == nil) + assert(t[false] == b) +end + diff --git a/test/misc/tonumber_scan.lua b/test/misc/tonumber_scan.lua new file mode 100644 index 0000000000..78e1ca3ee5 --- /dev/null +++ b/test/misc/tonumber_scan.lua @@ -0,0 +1,180 @@ +local ffi = require("ffi") +local bit = require("bit") + +ffi.cdef[[ +double strtod(const char *, char **); +]] + +local t = { + -- errors + false, "", + false, " ", + false, "+", + false, "z", + false, ".", + false, ".z", + false, "0.z", + false, ".0z", + false, "0xz", + false, "0x.z", + false, "0x0.z", + false, "0x.0z", + false, ".e5", + false, ".p4", + false, "1.p4", + false, "1.p+4", + false, "0x1.e+4", + false, "infi", + false, "+ 1", + false, "- 9", + -- misc + 0x3ff0000000000000ULL, " \t\n\v\f\r 1", + -- inf/nan + 0x7ff0000000000000ULL, "iNF", + 0xfff0000000000000ULL, "-Inf", + 0x7ff0000000000000ULL, "+iNfInItY", + 0xfff0000000000000ULL, "-INFINITY", + 0xfff8000000000000ULL, "naN", + 0xfff8000000000000ULL, "+NaN", + 0xfff8000000000000ULL, "-nAn", + -- smallest/largest numbers + 0x0000000000000000ULL, "0e1000", + 0x0000000000000000ULL, "0e-1000", + 0x0000000000000000ULL, "0x0p2000", + 0x0000000000000000ULL, "0x0p-2000", + 0x7ff0000000000000ULL, "1e1000", + 0x0000000000000000ULL, "1e-1000", + 0xfff0000000000000ULL, "-1e1000", +-- wrong for DUALNUM: 0x8000000000000000ULL, "-1e-1000", + 0x7ff0000000000000ULL, "0x1p2000", + 0x0000000000000000ULL, "0x1p-2000", + 0xfff0000000000000ULL, "-0x1p2000", +-- wrong for DUALNUM: 0x8000000000000000ULL, "-0x1p-2000", + 0x0010000000000000ULL, "2.2250738585072014e-308", + 0x7fefffffffffffffULL, "1.7976931348623158e+308", + 0x8000b8157268fdafULL, "-1e-309", + 0x000ac941b426dd3bULL, "1.5e-308", + 0x000ac941b426dd3bULL, "0x0.ac941b426dd3b7p-1022", + 0x0000000000000001ULL, "4.9406564584124654e-324", + 0x000f9c7573d7fe52ULL, "2.171e-308", + 0x241d21ecf36d4a22ULL, "1.0020284025808569e-134", + 0x0000000000000001ULL, "0x1p-1074", + 0x0000000000000000ULL, "0x1p-1075", + 0x0000000000000000ULL, "0x1p-1076", + 0x0000000000000000ULL, "0x0.ffffffffffffffffffffffffffp-1075", + 0x0000000000000000ULL, "0x1.00000000000000000000000000p-1075", + 0x0000000000000001ULL, "0x1.00000000000000000000000001p-1075", + 0x7fe0000000000000ULL, "0x1p1023", + 0x7ff0000000000000ULL, "0x1p1024", + 0x7ff0000000000000ULL, "0x1p1025", + 0x7ff0000000000000ULL, "0x3p1023", + 0x7ff0000000000000ULL, "0x3.ffffffffffffecp1023", + 0xfff0000000000000ULL, "-0xf7dcba98765432p969", + 0x7fefffffffffffffULL, "0x1.fffffffffffff0000000000000p1023", + 0x7fefffffffffffffULL, "0x1.fffffffffffff0000000000001p1023", + 0x7fefffffffffffffULL, "0x1.fffffffffffff7ffffffffffffp1023", + 0x7ff0000000000000ULL, "0x1.fffffffffffff8000000000000p1023", + 0x7fefffffffffffffULL, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0", + 0x7fefffffffffffffULL, "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791.999", + 0x7ff0000000000000ULL, "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792.0", + 0x3ff0000000000000ULL, "0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000p-1028", + 0x1214e2995454ee0bULL, "0."..string.rep("0", 220).."1"..string.rep("4", 800), + -- http://www.exploringbinary.com/15-digit-quick-and-dirty-conversions-dont-round-trip/ + 0x04409cf3929ffbc3ULL, "3.409452297963e-288", + 0x7fe02b4782a6c378ULL, "9.08344e+307", + 0x6e05e258a3929ee5ULL, "9.88819e+221", + -- http://www.exploringbinary.com/incorrectly-rounded-conversions-in-gcc-and-glibc/ + 0x3fe0000000000002ULL, "0.500000000000000166533453693773481063544750213623046875", + 0x42c0000000000002ULL, "3.518437208883201171875e13", + 0x404f44abd5aa7ca4ULL, "62.5364939768271845828", + 0x3e0bd5cbaef0fd0cULL, "8.10109172351e-10", + 0x3ff8000000000000ULL, "1.50000000000000011102230246251565404236316680908203125", + 0x433fffffffffffffULL, "9007199254740991.4999999999999999999999999999999995", + 0x7ecd2e77eb6e3fadULL, "6.253649397682718e+302", + 0x7ecd2e77eb6e3fadULL, "6.2536493976827180e+302", + -- http://www.exploringbinary.com/incorrectly-rounded-conversions-in-visual-c-plus-plus/ + 0x43405e6cec57761aULL, "9214843084008499", + 0x3fe0000000000002ULL, "0.500000000000000166533453693773481063544750213623046875", + 0x44997a3c7271b021ULL, "30078505129381147446200", + 0x4458180d5bad2e3eULL, "1777820000000000000001", + 0x3fe0000000000002ULL, "0.500000000000000166547006220929549868969843373633921146392822265625", + 0x3fe0000000000002ULL, "0.50000000000000016656055874808561867439493653364479541778564453125", + 0x3fd92bb352c4623aULL, "0.3932922657273", + -- http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + 0x0010000000000000ULL, "2.2250738585072012e-308", + -- http://www.exploringbinary.com/incorrectly-rounded-subnormal-conversions-in-java/ + 0x0000000008000000ULL, "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125e-316", + 0x0000000000010000ULL, "3.237883913302901289588352412501532174863037669423108059901297049552301970670676565786835742587799557860615776559838283435514391084153169252689190564396459577394618038928365305143463955100356696665629202017331344031730044369360205258345803431471660032699580731300954848363975548690010751530018881758184174569652173110473696022749934638425380623369774736560008997404060967498028389191878963968575439222206416981462690113342524002724385941651051293552601421155333430225237291523843322331326138431477823591142408800030775170625915670728657003151953664260769822494937951845801530895238439819708403389937873241463484205608000027270531106827387907791444918534771598750162812548862768493201518991668028251730299953143924168545708663913273994694463908672332763671875e-319", + 0x0000800000000100ULL, "6.953355807847677105972805215521891690222119817145950754416205607980030131549636688806115726399441880065386399864028691275539539414652831584795668560082999889551357784961446896042113198284213107935110217162654939802416034676213829409720583759540476786936413816541621287843248433202369209916612249676005573022703244799714622116542188837770376022371172079559125853382801396219552418839469770514904192657627060319372847562301074140442660237844114174497210955449896389180395827191602886654488182452409583981389442783377001505462015745017848754574668342161759496661766020028752888783387074850773192997102997936619876226688096314989645766000479009083731736585750335262099860150896718774401964796827166283225641992040747894382698751809812609536720628966577351093292236328125e-310", + 0x0000000000010800ULL, "3.339068557571188581835713701280943911923401916998521771655656997328440314559615318168849149074662609099998113009465566426808170378434065722991659642619467706034884424989741080790766778456332168200464651593995817371782125010668346652995912233993254584461125868481633343674905074271064409763090708017856584019776878812425312008812326260363035474811532236853359905334625575404216060622858633280744301892470300555678734689978476870369853549413277156622170245846166991655321535529623870646888786637528995592800436177901746286272273374471701452991433047257863864601424252024791567368195056077320885329384322332391564645264143400798619665040608077549162173963649264049738362290606875883456826586710961041737908872035803481241600376705491726170293986797332763671875e-319", + -- EGLIBC 2.16 tests + 0x4028b0a3d70a3d71ULL, "12.345", + 0x441ac4da03bc47e4ULL, "12.345e19", + 0xc197d78400000000ULL, "-.1e+9", + 0x3fc0000000000000ULL, ".125", + 0x4415af1d78b58c40ULL, "1e20", + 0x0000000000000000ULL, "0e-19", + 0x3051144f2d9a718bULL, "5.9e-76", + 0x4024000000000000ULL, "0x1.4p+3", + 0x4024000000000000ULL, "0xAp0", + 0x4024000000000000ULL, "0x0Ap0", + 0x4024000000000000ULL, "0x0A", + 0x4064000000000000ULL, "0xA0", + 0x4064000000000000ULL, "0x0.A0p8", + 0x4064000000000000ULL, "0x0.50p9", + 0x4064000000000000ULL, "0x0.28p10", + 0x4064000000000000ULL, "0x0.14p11", + 0x4064000000000000ULL, "0x0.0A0p12", + 0x4064000000000000ULL, "0x0.050p13", + 0x4064000000000000ULL, "0x0.028p14", + 0x4064000000000000ULL, "0x0.014p15", + 0x4064000000000000ULL, "0x00.00A0p16", + 0x4064000000000000ULL, "0x00.0050p17", + 0x4064000000000000ULL, "0x00.0028p18", + 0x4064000000000000ULL, "0x00.0014p19", + 0x0008000000000000ULL, "0x1p-1023", + 0x0008000000000000ULL, "0x0.8p-1022", + 0x3ff0000140000000ULL, "0x80000Ap-23", + 0x0000000000000000ULL, "1e-324", + 0x4370000000000000ULL, "0x100000000000008p0", + 0x4370000000000000ULL, "0x100000000000008.p0", + 0x4370000000000000ULL, "0x100000000000008.00p0", + 0x43f0000000000000ULL, "0x10000000000000800p0", + 0x43f0000000000001ULL, "0x10000000000000801p0", + -- Fuzzing + 0x699783fbf2d24ea5ULL, "449999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", + 0x43f158e460913d00ULL, "2e19", +} + +local function tohex64(x) + return "0x"..bit.tohex(tonumber(x/2LL^32))..bit.tohex(tonumber(x%2LL^32)).."ULL" +end + +local conv = tonumber + +if arg and arg[1] == "strtod" then + local e = ffi.new("char *[1]") + function conv(s) + local d = ffi.C.strtod(s, e) + return (e[0][0] == 0 and #s ~= 0) and d or nil + end +end + +local u = ffi.new("union { double d; uint64_t x; }") + +for i=1,#t,2 do + local y, s = t[i], t[i+1] + local d = conv(s) + local ok + if d == nil then + ok = (y == false) + else + u.d = d + ok = (y == u.x) + end + if not ok then + io.write('FAIL: "', s, '"\n GOT: ', d and tohex64(u.x) or "nil", " OK: ", y and tohex64(y) or "nil", "\n\n") +-- print(" "..tohex64(u.x)..", \""..s.."\",") + end +end + diff --git a/test/misc/tonumber_tostring.lua b/test/misc/tonumber_tostring.lua new file mode 100644 index 0000000000..d611b2c3c4 --- /dev/null +++ b/test/misc/tonumber_tostring.lua @@ -0,0 +1,82 @@ + +do + local x = 0 + for i=1,100 do x = x + tonumber(i) end + assert(x == 5050) +end + +do + local x = 0 + for i=1.5,100.5 do x = x + tonumber(i) end + assert(x == 5100) +end + +do + local t = {} + for i=1,100 do t[i] = tostring(i) end + local x = 0 + for i=1,100 do assert(type(t[i]) == "string"); x = x + tonumber(t[i]) end + assert(x == 5050) +end + +do + local t = {} + for i=1,100 do t[i] = tostring(i+0.5) end + local x = 0 + for i=1,100 do assert(type(t[i]) == "string"); x = x + tonumber(t[i]) end + assert(x == 5100) +end + +do + for i=1,100 do assert(tonumber({}) == nil) end +end + +do + local t = {} + for i=1,100 do t[i] = tostring(i) end + for i=1,100 do t[i] = tostring(t[i]) end + local x = 0 + for i=1,100 do assert(type(t[i]) == "string"); x = x + t[i] end + assert(x == 5050) +end + +do + local mt = { __tostring = function(t) return tostring(t[1]) end } + local t = {} + for i=1,100 do t[i] = setmetatable({i}, mt) end + for i=1,100 do t[i] = tostring(t[i]) end + local x = 0 + for i=1,100 do assert(type(t[i]) == "string"); x = x + t[i] end + assert(x == 5050) +end + +do + local r = setmetatable({}, + { __call = function(x, t) return tostring(t[1]) end }) + local mt = { __tostring = r } + local t = {} + for i=1,100 do t[i] = setmetatable({i}, mt) end + for i=1,100 do t[i] = tostring(t[i]) end + local x = 0 + for i=1,100 do assert(type(t[i]) == "string"); x = x + t[i] end + assert(x == 5050) +end + +do + local x = false + local co = coroutine.create(function() print(1) end) + debug.setfenv(co, setmetatable({}, { __index = { + tostring = function() x = true end }})) + coroutine.resume(co) + assert(x == true) +end + +do + assert(tonumber(111, 2) == 7) +end + +do + local t = setmetatable({}, { __tostring = "" }) + assert(pcall(function() tostring(t) end) == false) +end + diff --git a/test/misc/uclo.lua b/test/misc/uclo.lua new file mode 100644 index 0000000000..bd9bd24299 --- /dev/null +++ b/test/misc/uclo.lua @@ -0,0 +1,91 @@ + +local function test_for() + local z1, z2 + for i=1,10 do + local function f() return i end + if z1 then z2 = f else z1 = f end + end + assert(z1() == 1) + assert(z2() == 10) +end + +local function test_while() + local z1, z2 + local i = 1 + while i <= 10 do + local j = i + local function f() return j end + if z1 then z2 = f else z1 = f end + i = i + 1 + end + assert(z1() == 1) + assert(z2() == 10) +end + +local function test_repeat() + local z1, z2 + local i = 1 + repeat + local j = i + local function f() return j end + if z1 then z2 = f else z1 = f end + i = i + 1 + until i > 10 + assert(z1() == 1) + assert(z2() == 10) +end + +local function test_func() + local function ff(x) + return function() return x end + end + local z1, z2 + for i=1,10 do + local f = ff(i) + if z1 then z2 = f else z1 = f end + end + assert(z1() == 1) + assert(z2() == 10) +end + +test_for() +test_while() +test_repeat() +test_func() + +do + local function f1(a) + if a > 0 then + local b = f1(a - 1) + return function() + if type(b) == "function" then + return a + b() + end + return a + b + end + end + return a + end + + local function f2(a) + return f1(a)() + end + + for i = 1, 41 do + local r = f2(4) + f2(4) + end +end + +-- Don't mark upvalue as immutable if written to after prototype definition. +do + local x = 1 + local function f() + local y = 0 + for i=1,100 do y=y+x end + return y + end + assert(f() == 100) + x = 2 + assert(f() == 200) +end + diff --git a/test/misc/unordered.lua b/test/misc/unordered.lua new file mode 100644 index 0000000000..9e71046de9 --- /dev/null +++ b/test/misc/unordered.lua @@ -0,0 +1,79 @@ + +local function check(a, b) + if a ~= b then + error("check failed with "..tostring(a).." ~= "..tostring(b), 2) + end +end + +local x,y = 0/0,1 + +check(xx, false) +check(x>=x, false) +check(x==x, false) +check(x~=x, true) + +check(xy, false) +check(x>=y, false) +check(x==y, false) +check(x~=y, true) + +check(yx, false) +check(y>=x, false) +check(y==x, false) +check(y~=x, true) + +check(x<1, false) +check(x<=1, false) +check(x>1, false) +check(x>=1, false) +check(x==1, false) +check(x~=1, true) + +check(1x, false) +check(1>=x, false) +check(1==x, false) +check(1~=x, true) + +check(not (xx), true) +check(not (x>=x), true) +check(not (x==x), true) +check(not (x~=x), false) + +check(not (xy), true) +check(not (x>=y), true) +check(not (x==y), true) +check(not (x~=y), false) + +check(not (yx), true) +check(not (y>=x), true) +check(not (y==x), true) +check(not (y~=x), false) + +check(not (x<1), true) +check(not (x<=1), true) +check(not (x>1), true) +check(not (x>=1), true) +check(not (x==1), true) +check(not (x~=1), false) + +check(not (1x), true) +check(not (1>=x), true) +check(not (1==x), true) +check(not (1~=x), false) + diff --git a/test/misc/unordered_jit.lua b/test/misc/unordered_jit.lua new file mode 100644 index 0000000000..5ff1a1bafd --- /dev/null +++ b/test/misc/unordered_jit.lua @@ -0,0 +1,96 @@ + +local nan = 0/0 +local t = {} +for i=1,100 do t[i] = i+0.5 end +for i=101,200 do t[i] = nan end + +do + local z = 0 + for i=1,200 do if t[i] > 1000 then z=i end end + assert(z == 0) +end + +do + local z = 0 + for i=1,200 do if not (t[i] < 1000) then z=i end end + assert(z == 200) +end + +do + local z = 0 + for i=1,200 do if t[i] <= 1000 then z=i end end + assert(z == 100) +end + +do + local z = 0 + for i=1,200 do if not (t[i] >= 1000) then z=i end end + assert(z == 200) +end + +do + local z = 0 + for i=1,200 do if t[i] > 0 then z=i end end + assert(z == 100) +end + +do + local z = 0 + for i=1,200 do if not (t[i] < 0) then z=i end end + assert(z == 200) +end + +do + local z = 0 + for i=1,200 do if t[i] <= 0 then z=i end end + assert(z == 0) +end + +do + local z = 0 + for i=1,200 do if not (t[i] >= 0) then z=i end end + assert(z == 200) +end + +do local z; for i=1,100 do z = 0/0 end; assert(z ~= z) end + +do local z; for i=1,100 do z = nan == nan end; assert(z == false) end +do local z; for i=1,100 do z = nan == 1 end; assert(z == false) end +do local z; for i=1,100 do z = 1 == nan end; assert(z == false) end + +do local z; for i=1,100 do z = nan ~= nan end; assert(z == true) end +do local z; for i=1,100 do z = nan ~= 1 end; assert(z == true) end +do local z; for i=1,100 do z = 1 ~= nan end; assert(z == true) end + +do local z; for i=1,100 do z = nan < nan end; assert(z == false) end +do local z; for i=1,100 do z = nan < 1 end; assert(z == false) end +do local z; for i=1,100 do z = 1 < nan end; assert(z == false) end + +do local z; for i=1,100 do z = not (nan < nan) end; assert(z == true) end +do local z; for i=1,100 do z = not (nan < 1) end; assert(z == true) end +do local z; for i=1,100 do z = not (1 < nan) end; assert(z == true) end + +do local z; for i=1,100 do z = nan > nan end; assert(z == false) end +do local z; for i=1,100 do z = nan > 1 end; assert(z == false) end +do local z; for i=1,100 do z = 1 > nan end; assert(z == false) end + +do local z; for i=1,100 do z = not (nan > nan) end; assert(z == true) end +do local z; for i=1,100 do z = not (nan > 1) end; assert(z == true) end +do local z; for i=1,100 do z = not (1 > nan) end; assert(z == true) end + +do local z; for i=1,100 do z = nan <= nan end; assert(z == false) end +do local z; for i=1,100 do z = nan <= 1 end; assert(z == false) end +do local z; for i=1,100 do z = 1 <= nan end; assert(z == false) end + +do local z; for i=1,100 do z = not (nan <= nan) end; assert(z == true) end +do local z; for i=1,100 do z = not (nan <= 1) end; assert(z == true) end +do local z; for i=1,100 do z = not (1 <= nan) end; assert(z == true) end + +do local z; for i=1,100 do z = nan >= nan end; assert(z == false) end +do local z; for i=1,100 do z = nan >= 1 end; assert(z == false) end +do local z; for i=1,100 do z = 1 >= nan end; assert(z == false) end + +do local z; for i=1,100 do z = not (nan >= nan) end; assert(z == true) end +do local z; for i=1,100 do z = not (nan >= 1) end; assert(z == true) end +do local z; for i=1,100 do z = not (1 >= nan) end; assert(z == true) end + diff --git a/test/misc/vararg_jit.lua b/test/misc/vararg_jit.lua new file mode 100644 index 0000000000..98874a31bf --- /dev/null +++ b/test/misc/vararg_jit.lua @@ -0,0 +1,95 @@ + +do + local function f(a, b, c, ...) + assert(c == nil) + assert(a == 100-b) + return 100-a, 100-b + end + for i=1,100 do + local x, y = f(i, 100-i) + assert(x == 100-i) + assert(y == i) + end +end + +do + local function f(a, b, ...) + if a > b then return b end + return a + end + local x = 0 + for i=1,200 do + x = x + f(i, 100, 99, 88, 77) + end + assert(x == 15050) +end + +do + local function f(a, b, ...) + local c, d = ... + if c > d then return d end + return c + end + local x = 0 + for i=1,200 do + x = x + f(77, 88, i, 100) + end + assert(x == 15050) +end + +do + local function f(a, b, ...) + if a > b then end + return ... + end + local x = 0 + for i=1,200 do + x = x + f(i, 100, i, 100) + assert(f(i, 100) == nil) + assert(f(i, 100, 2) == 2) + end + assert(x == 20100) +end + +do + local function f(a, ...) + local x, y = 0, 0 + for i=1,100 do + local b, c = ... + x = x + b + y = y + c + end + assert(x == 200 and y == 300) + end + f(1, 2, 3) +end + +do + local function f(a, ...) + local t = {[0]=9, 9} + local v, w, x, y = 0, 0, 0, 0 + for i=1,100 do + v, w = ... + t[0] = 9; t[1] = 9; + x, y = ... + end + assert(v == 2 and w == 3 and x == 2 and y == 3) + end + f(1, 2, 3) +end + +do + local function f(a, b, ...) + for i=1,100 do + local c, d = ... + assert(a == c); + assert(b == d); + end + end + f(2, 3, 2, 3) + f(2, nil, 2) + f(nil, nil) + f(nil) + f() +end + diff --git a/test/misc/wbarrier.lua b/test/misc/wbarrier.lua new file mode 100644 index 0000000000..5536625a36 --- /dev/null +++ b/test/misc/wbarrier.lua @@ -0,0 +1,7 @@ +local t={} +for i=1,20000 do + t[i] = tostring(i) +end +for i=1,#t do + assert(t[i] == tostring(i)) +end diff --git a/test/misc/wbarrier_jit.lua b/test/misc/wbarrier_jit.lua new file mode 100644 index 0000000000..2c8dd7fbcb --- /dev/null +++ b/test/misc/wbarrier_jit.lua @@ -0,0 +1,18 @@ + +do + local t = {[0]={}} + for i=1,1e5 do t[i] = {t[i-1]} end + for i=1,1e5 do assert(t[i][1] == t[i-1]) end +end + +do + local f + do + local x = 0 + function f() + for i=1,1e5 do x = {i} end + end + end + f() +end + diff --git a/test/misc/wbarrier_obar.lua b/test/misc/wbarrier_obar.lua new file mode 100644 index 0000000000..258db2158e --- /dev/null +++ b/test/misc/wbarrier_obar.lua @@ -0,0 +1,22 @@ +-- DSE of USTORE must eliminate OBAR, too. + +if jit and jit.opt then pcall(jit.opt.start, "-sink") end + +local f +do + local x + f = function() + local y = 0 + for i=1,10000 do + x = {1} + if y > 0 then end + x = 1 + end + end +end + +collectgarbage() +collectgarbage("setstepmul", 1) +collectgarbage("restart") +f() + diff --git a/test/misc/xpcall_jit.lua b/test/misc/xpcall_jit.lua new file mode 100644 index 0000000000..61ffc8e64c --- /dev/null +++ b/test/misc/xpcall_jit.lua @@ -0,0 +1,86 @@ + +local function tr(err) return "tr"..err end + +do + local function f(x) return x*x end + local x = 0 + for i=1,100 do + local ok1, ok2, ok3, y = xpcall(xpcall, tr, xpcall, tr, f, tr, i) + if not ok1 or not ok2 or not ok3 then break end + x = x + y + end + assert(x == 338350) +end + +do + local x = 0 + for i=1,100 do + local ok1, ok2, ok3, y = xpcall(xpcall, tr, xpcall, tr, math.sqrt, tr, i*i) + if not ok1 or not ok2 or not ok3 then break end + x = x + y + end + assert(x == 5050) +end + +do + local function f(x) + if x >= 150 then error("test", 0) end + return x end + local x = 0 + for i=1,200 do + local ok1, ok2, ok3, y = xpcall(xpcall, tr, xpcall, tr, f, tr, i) + if not ok1 or not ok2 or not ok3 then + assert(ok1 and ok2 and not ok3) + assert(y == "trtest") + break + end + x = x + y + end + assert(x == 11175) +end + +do + local function f(x) + if x >= 150 then return x*x end + return x + end + local x = 0 + for i=1,200 do + local ok1, ok2, ok3, y = xpcall(xpcall, tr, xpcall, tr, f, tr, i) + if not ok1 or not ok2 or not ok3 then break end + x = x + y + end + assert(x == 1584100) +end + +do + local function f(x) + if x >= 150 then + if x >= 175 then error("test", 0) end + return x*x + end + return x + end + local x = 0 + for i=1,200 do + local ok1, ok2, ok3, y = xpcall(xpcall, tr, xpcall, tr, f, tr, i) + if not ok1 or not ok2 or not ok3 then + assert(ok1 and ok2 and not ok3) + assert(y == "trtest") + -- note: no break, so we get an exit to interpreter + else + x = x + y + end + end + assert(x == 668575) +end + +do + local x = 0 + for i=1,100 do + -- Test xpcall swap after recorder error. + local ok1, ok2, ok3, err = xpcall(xpcall, tr, xpcall, tr, error, tr, "test", 0) + assert(ok1 and ok2 and not ok3 and err == "trtest") + end +end + diff --git a/test/sysdep/catch_cpp.lua b/test/sysdep/catch_cpp.lua new file mode 100644 index 0000000000..b2251009a5 --- /dev/null +++ b/test/sysdep/catch_cpp.lua @@ -0,0 +1,71 @@ + +local cp = require("cpptest") + +do + local a, b = pcall(cp.catch, function() return "x" end) + assert(a == true and b == "x") +end + +do + local a, b = pcall(function() cp.throw("foo") end) + assert(a == false and b == "C++ exception") +end + +local unwind +do + local a, b = pcall(cp.catch, function() cp.throw("foo") end) + unwind = a + assert((a == false and b == "C++ exception") or (a == true and b == "foo")) +end + +do + local st = cp.alloc(function() return cp.isalloc() end) + assert(st == true) + assert(cp.isalloc() == false) +end + +do + local a, b = pcall(cp.alloc, function() + assert(cp.isalloc() == true) + return "foo", cp.throw + end) + assert(a == false and b == "C++ exception") + assert(cp.isalloc() == false) +end + +if unwind then + local a, b = pcall(cp.alloc, function() + assert(cp.isalloc() == true) + return "foo", error + end) + assert(a == false and b == "foo") + assert(cp.isalloc() == false) +end + +do + local a,b,c,d,e,f = cp.usereg(100, 50, function() end, false) + assert(a==164 and b==312 and c==428 and d==3696 and e==404 and f==404) +end + +do + local function test() + cp.usereg(100, 40, error, "foo") + end + local a,b,c,d,e,f = cp.usereg(100, 51, test, false) + assert(a==164 and b==312 and c==428 and d==3696 and e==404 and f==404) +end + +do + local t = {}; + t.t = t; + local function foo() + for i=1,100 do + local a,b,c,d,e,f = t, t.t, t.t.t, t.t.t.t, t.t.t.t.t, t.t.t.t.t.t + local g,h,j,k,l = f.t, f.t.t, f.t.t.t, f.t.t.t.t, f.t.t.t.t.t + local m = { a,b,c,d,e,f,g,h,j,k,l } + end + end + local a,b,c,d,e,f = cp.usereg(100, 50, foo, false) + assert(a==164 and b==312 and c==428 and d==3696 and e==404 and f==404) +end + diff --git a/test/sysdep/ffi_include_gtk.lua b/test/sysdep/ffi_include_gtk.lua new file mode 100644 index 0000000000..a4bfceacaf --- /dev/null +++ b/test/sysdep/ffi_include_gtk.lua @@ -0,0 +1,9 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +if cdefs == "" then + cdefs = "-pthread -D_REENTRANT -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/directfb -I/usr/include/libpng12 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include -I/usr/include/gdk-pixbuf-2.0" +end + +include"/usr/include/gtk-2.0/gtk/gtk.h" diff --git a/test/sysdep/ffi_include_std.lua b/test/sysdep/ffi_include_std.lua new file mode 100644 index 0000000000..b88c82bdae --- /dev/null +++ b/test/sysdep/ffi_include_std.lua @@ -0,0 +1,36 @@ +local ffi = require("ffi") + +dofile("../common/ffi_util.inc") + +do + local fp = assert(io.open("/tmp/__tmp.c", "w")) + fp:write[[ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +]] + fp:close() + + local flags = ffi.abi("32bit") and "-m32" or "-m64" + fp = assert(io.popen("cc -E -P -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE /tmp/__tmp.c "..flags)) + local s = fp:read("*a") + fp:close() + os.remove("/tmp/__tmp.c") + ffi.cdef(s) +end + diff --git a/test/sysdep/ffi_lib_c.lua b/test/sysdep/ffi_lib_c.lua new file mode 100644 index 0000000000..a368d750f7 --- /dev/null +++ b/test/sysdep/ffi_lib_c.lua @@ -0,0 +1,87 @@ +local ffi = require("ffi") + +ffi.cdef[[ +// libc/libm +int sprintf(char *buf, const char *fmt, ...); +double pow(double x, double y); +int rmdir(const char *name); +int errno; + +// Windows +unsigned int GetSystemDirectoryA(char *buf, unsigned int sz); +char *CharUpperA(char *str); +int GdiFlush(void); +int _rmdir(const char *name); +static const int _O_TEXT = 0x4000; +static const int _O_BINARY = 0x8000; +int *_errno(void); +int _fmode; + +// Lua/C API +typedef struct lua_State lua_State; +typedef double lua_Number; +lua_State *luaL_newstate(void); +void luaL_openlibs(lua_State *L); +void lua_close(lua_State *L); +int luaL_loadstring(lua_State *L, const char *s); +int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc); +lua_Number lua_tonumber(lua_State *L, int idx); +]] + +local C = ffi.C + +do + local buf = ffi.new("char[?]", 100) + local n = C.sprintf(buf, "test %g %s", 12.5, "foo") + assert(ffi.string(buf, n) == "test 12.5 foo") +end + +assert(ffi.C.pow(2.5, 5) == 97.65625) + +if ffi.abi("win") then + do + local buf = ffi.new("char[?]", 4, "abc") + C.CharUpperA(buf) + assert(ffi.string(buf) == "ABC") + end + + do + local buf = ffi.new("char[?]", 256) + local len = C.GetSystemDirectoryA(buf, 255) + local s = ffi.string(buf, len) + assert(string.find(string.lower(s), "\\system32")) + end + + assert(C.GdiFlush() == 1) + + assert(ffi.C._rmdir("/tmp/does_not_exist") == -1) + assert(ffi.C._errno()[0] == 2) + + ffi.C._fmode = ffi.C._O_BINARY + assert(ffi.C._fmode == ffi.C._O_BINARY) + ffi.C._fmode = ffi.C._O_TEXT +else + assert(ffi.C.rmdir("/tmp/does_not_exist") == -1) + assert(ffi.C.errno == 2) + + ffi.C.errno = 17 + assert(ffi.C.errno == 17) + ffi.C.errno = 0 +end + +do + local L = C.luaL_newstate() + local s = "local x = 0; for i=1,100 do x=x+i end; return x" + C.luaL_openlibs(L) + assert(C.luaL_loadstring(L, s) == 0) + assert(C.lua_pcall(L, 0, 1, 0) == 0) + assert(C.lua_tonumber(L, -1) == 5050) + C.lua_close(L) +end + +do + if not (ffi.os == "Windows" or ffi.os == "Other") then + ffi.load("pthread") + end +end + diff --git a/test/sysdep/ffi_lib_z.lua b/test/sysdep/ffi_lib_z.lua new file mode 100644 index 0000000000..69a19aedcd --- /dev/null +++ b/test/sysdep/ffi_lib_z.lua @@ -0,0 +1,107 @@ +local ffi = require("ffi") + +local compress, uncompress + +if ffi.abi("win") then + + ffi.cdef[[ + int RtlGetCompressionWorkSpaceSize(uint16_t fmt, + unsigned long *wsbufsz, unsigned long *wsfragsz); + int RtlCompressBuffer(uint16_t fmt, + const uint8_t *src, unsigned long srclen, + uint8_t *dst, unsigned long dstsz, + unsigned long chunk, unsigned long *dstlen, void *workspace); + int RtlDecompressBuffer(uint16_t fmt, + uint8_t *dst, unsigned long dstsz, + const uint8_t *src, unsigned long srclen, + unsigned long *dstlen); + ]] + + local ntdll = ffi.load("ntdll") + + local fmt = 0x0102 + local workspace + do + local res = ffi.new("unsigned long[2]") + ntdll.RtlGetCompressionWorkSpaceSize(fmt, res, res+1) + workspace = ffi.new("uint8_t[?]", res[0]) + end + + function compress(txt) + local buf = ffi.new("uint8_t[?]", 4096) + local buflen = ffi.new("unsigned long[1]") + local res = ntdll.RtlCompressBuffer(fmt, txt, #txt, buf, 4096, + 4096, buflen, workspace) + assert(res == 0) + return ffi.string(buf, buflen[0]) + end + + function uncompress(comp, n) + local buf = ffi.new("uint8_t[?]", n) + local buflen = ffi.new("unsigned long[1]") + local res = ntdll.RtlDecompressBuffer(fmt, buf, n, comp, #comp, buflen) + assert(res == 0) + return ffi.string(buf, buflen[0]) + end + +else + + ffi.cdef[[ + unsigned long compressBound(unsigned long sourceLen); + int compress2(uint8_t *dest, unsigned long *destLen, + const uint8_t *source, unsigned long sourceLen, int level); + int uncompress(uint8_t *dest, unsigned long *destLen, + const uint8_t *source, unsigned long sourceLen); + ]] + + local zlib = ffi.load("z") + + function compress(txt) + local n = tonumber(zlib.compressBound(#txt)) + local buf = ffi.new("uint8_t[?]", n) + local buflen = ffi.new("unsigned long[1]", n) + local res = zlib.compress2(buf, buflen, txt, #txt, 9) + assert(res == 0) + return ffi.string(buf, tonumber(buflen[0])) + end + + function uncompress(comp, n) + local buf = ffi.new("uint8_t[?]", n) + local buflen = ffi.new("unsigned long[1]", n) + local res = zlib.uncompress(buf, buflen, comp, #comp) + assert(res == 0) + return ffi.string(buf, tonumber(buflen[0])) + end + +end + + local txt = [[Rebellious subjects, enemies to peace, +Profaners of this neighbour-stained steel,-- +Will they not hear? What, ho! you men, you beasts, +That quench the fire of your pernicious rage +With purple fountains issuing from your veins, +On pain of torture, from those bloody hands +Throw your mistemper'd weapons to the ground, +And hear the sentence of your moved prince. +Three civil brawls, bred of an airy word, +By thee, old Capulet, and Montague, +Have thrice disturb'd the quiet of our streets, +And made Verona's ancient citizens +Cast by their grave beseeming ornaments, +To wield old partisans, in hands as old, +Canker'd with peace, to part your canker'd hate: +If ever you disturb our streets again, +Your lives shall pay the forfeit of the peace. +For this time, all the rest depart away: +You Capulet; shall go along with me: +And, Montague, come you this afternoon, +To know our further pleasure in this case, +To old Free-town, our common judgment-place. +Once more, on pain of death, all men depart.]] +txt = txt..txt..txt..txt + +local c = compress(txt) +assert(2*#c < #txt) +local txt2 = uncompress(c, #txt) +assert(txt2 == txt) + diff --git a/test/unportable/ffi_arith_int64.lua b/test/unportable/ffi_arith_int64.lua new file mode 100644 index 0000000000..c05e02a974 --- /dev/null +++ b/test/unportable/ffi_arith_int64.lua @@ -0,0 +1,68 @@ +local ffi = require("ffi") + +local int = ffi.typeof("int") + +local inp = { + 0, 0.5, -0.5, 1.5, -1.5, 1, -1, 2, -2, 37, -37, false, + int(0), int(1), int(-1), int(2), int(-2), int(37), int(-37), false, + 0ll, 1ll, -1ll, 2ll, -2ll, 37ll, -37ll, false, + 0ull, 1ull, -1ull, 2ull, -2ull, 37ull, -37ull, +} + +local function cksum(s, r) + local z = 0 + for i=1,#s do z = (z + string.byte(s, i)*i) % 2147483629 end + if z ~= r then + error("test failed (got "..z..", expected "..r..") for:\n"..s, 3) + end +end + +local function tostr(n) + if type(n) == "cdata" then return tostring(n) + elseif n ~= n then return "nan" + else return string.format("%+1.5g", n) end +end + +local function check(f, expected, y) + local inp = inp + local out = {} + for i=1,#inp do + if inp[i] then out[i] = tostr(f(inp[i], y)) else out[i] = "\n" end + end + local got = string.gsub(table.concat(out, " ").."\n", "\n ", "\n") + cksum(got, expected) +end + +jit.off(check) + +local function check2(f, exparray) + local k = 1 + for j=1,#inp do + local y = inp[j] + if y then + check(f, exparray[k], y) + k = k + 1 + end + end +end + +check(function(x) return -x end, 1174528) + +check2(function(x, y) return x+y end, +{1171039,1239261,1239303,1011706,1490711,949996,1415869,756412,1682910,768883,2201023,1265370,1015700,1556902,807607,1862947,814710,2423097,1265370,1015700,1556902,807607,1862947,814710,2423097,4833809,2909723,7784653,1736671,10743770,1126700,13324037,}) + +check2(function(x, y) return x-y end, +{1171039,1239303,1239261,1490711,1011706,1415869,949996,1682910,756412,2201023,768883,1265370,1556902,1015700,1862947,807607,2423097,814710,1265370,1556902,1015700,1862947,807607,2423097,814710,4833809,7784653,2909723,10743770,1736671,13324037,1126700,}) + +check2(function(x, y) return x*y end, +{470257,637182,637132,1308150,1311627,1171039,1174528,1083877,1087553,1561321,1564869,564568,1265370,1269122,1265037,1268973,1643392,1647266,564568,1265370,1269122,1265037,1268973,1643392,1647266,827768,4833809,4847593,4823713,4838210,5230281,5244035,}) + +check2(function(x, y) return x/y end, +{7946210,7360895,7360865,1580465,927251,1171039,622069,1252901,704706,1542087,960011,14749620,1265370,695208,1188639,661058,1049280,587329,14749620,1265370,695208,1188639,661058,1049280,587329,15042810,4833809,828129,4559889,828509,4208862,828929,}) + + +check2(function(x, y) return x%y end, +{7653740,7304160,7304160,527871,851988,527061,850910,556674,717022,610671,613599,14749620,564568,894526,618652,785052,641760,644574,14749620,564568,894526,618652,785052,641760,644574,15042810,827768,2913108,829285,1737261,951059,959905,}) + +check2(function(x, y) return x^y end, +{471871,702627,720692,1385612,1803393,1171039,1772007,763817,1583994,4486762,2380423,566647,1265370,2319256,770581,1990479,4566660,2319835,566647,1265370,2319256,770581,1990479,4566660,2319835,830322,4833809,4644705,1071753,2822313,7709069,4647021,}) diff --git a/test/unportable/math_special.lua b/test/unportable/math_special.lua new file mode 100644 index 0000000000..49161014a7 --- /dev/null +++ b/test/unportable/math_special.lua @@ -0,0 +1,55 @@ + +local inp = { 0, -"0", 0.5, -0.5, 1, -1, 1/0, -1/0, 0/0 } + +local function tostr(n) + if n == 0 and 1/n < 0 then return "-0" + elseif 1/n == 0 then return n < 0 and "-inf" or "+inf" + elseif n ~= n then return "nan" + else return string.format("%+1.5g", n) end +end + +local function check(f, expected) + local inp = inp + local out = {} + for i=1,#inp do out[i] = tostr(f(inp[i])) end + local got = table.concat(out, " ") + if got ~= expected then + error("got: \""..got.."\"\nexpected: \""..expected.."\"", 2) + end +end + +check(function(x) return x end, "+0 -0 +0.5 -0.5 +1 -1 +inf -inf nan") + +local powcheck = { + "+1 +1 +1 +1 +1 +1 +1 +1 +1", + "+1 +1 +1 +1 +1 +1 +1 +1 +1", + "+0 +0 +0.70711 nan +1 nan +inf +inf nan", + "+inf +inf +1.4142 nan +1 nan +0 +0 nan", + "+0 -0 +0.5 -0.5 +1 -1 +inf -inf nan", + "+inf -inf +2 -2 +1 -1 +0 -0 nan", + "+0 +0 +0 +0 +1 +1 +inf +inf nan", + "+inf +inf +inf +inf +1 +1 +0 +0 nan", + "nan nan nan nan +1 nan nan nan nan", +} +for j=1,#inp do + local y = inp[j] + check(function(x) return x^y end, powcheck[j]) +end + +check(math.abs, "+0 +0 +0.5 +0.5 +1 +1 +inf +inf nan") +check(math.floor, "+0 -0 +0 -1 +1 -1 +inf -inf nan") +check(math.ceil, "+0 -0 +1 -0 +1 -1 +inf -inf nan") +check(math.sqrt, "+0 -0 +0.70711 nan +1 nan +inf nan nan") +check(math.sin, "+0 -0 +0.47943 -0.47943 +0.84147 -0.84147 nan nan nan") +check(math.cos, "+1 +1 +0.87758 +0.87758 +0.5403 +0.5403 nan nan nan") +check(math.tan, "+0 -0 +0.5463 -0.5463 +1.5574 -1.5574 nan nan nan") +check(math.asin, "+0 -0 +0.5236 -0.5236 +1.5708 -1.5708 nan nan nan") +check(math.acos, "+1.5708 +1.5708 +1.0472 +2.0944 +0 +3.1416 nan nan nan") +check(math.atan, "+0 -0 +0.46365 -0.46365 +0.7854 -0.7854 +1.5708 -1.5708 nan") +check(math.log, "-inf -inf -0.69315 nan +0 nan +inf nan nan") +check(math.log10, "-inf -inf -0.30103 nan +0 nan +inf nan nan") +check(math.exp, "+1 +1 +1.6487 +0.60653 +2.7183 +0.36788 +inf +0 nan") + +-- Pointless: deg, rad, min, max, pow +-- LATER: %, fmod, frexp, ldexp, modf, sinh, cosh, tanh +