├── test ├── ffi │ ├── shared.expected │ ├── libTest.c │ └── makefile ├── generated │ └── .gitignore ├── imports │ ├── cyclic.bqn │ ├── cyclic_A.bqn │ ├── cyclic_B.bqn │ ├── show.bqn │ └── notCyclic.bqn ├── cases │ ├── fuzz │ │ ├── join.bqn │ │ ├── shift.bqn │ │ ├── insert.bqn │ │ ├── member-of.bqn │ │ ├── group.bqn │ │ ├── bitarr-fill.bqn │ │ ├── arith.bqn │ │ ├── scan.bqn │ │ ├── hash.bqn │ │ ├── bitcpy.bqn │ │ ├── equal.bqn │ │ ├── squeezeValid.bqn │ │ ├── copy.bqn │ │ ├── is-sorted.bqn │ │ ├── cmp.bqn │ │ ├── hashmap.bqn │ │ ├── random.bqn │ │ ├── select-cells.bqn │ │ ├── squeezeExact.bqn │ │ └── bit.bqn │ ├── unaligned.bqn │ ├── usz32.bqn │ ├── gc.bqn │ ├── build-specific │ │ ├── test_range.bqn │ │ └── test_group_stat.bqn │ ├── proper-fills.bqn │ ├── undo.bqn │ ├── unnecessary │ │ └── fuzz-slash.bqn │ ├── imports.bqn │ ├── patterns.bqn │ ├── perf.bqn │ ├── hash.bqn │ └── fills.bqn ├── mainCfgs.sh ├── joinReuse.bqn ├── x86Cfgs.sh ├── precompiled.bqn ├── readTests.bqn ├── utils.bqn ├── README.md └── moreCfgs.sh ├── src ├── singeli │ ├── src │ │ ├── clmul.singeli │ │ ├── debug.singeli │ │ ├── dyarith.singeli │ │ ├── spaced.singeli │ │ ├── monarith.singeli │ │ ├── f64.singeli │ │ ├── sort.singeli │ │ ├── bmi.singeli │ │ ├── avx.singeli │ │ ├── vecfold.singeli │ │ ├── group.singeli │ │ ├── bits.singeli │ │ ├── cbqnDefs.singeli │ │ ├── selfsearch.singeli │ │ ├── bit_arith.singeli │ │ ├── avx512.singeli │ │ ├── scan_common.singeli │ │ ├── bitops.singeli │ │ ├── copy.singeli │ │ ├── avx2.singeli │ │ ├── hashtab.singeli │ │ ├── sse.singeli │ │ └── equal.singeli │ └── c │ │ ├── cmp.c │ │ └── arithdDispatch.h ├── utils │ ├── mem.h │ ├── time.h │ ├── cstr.h │ ├── interrupt.h │ ├── each.h │ ├── utf.h │ ├── toplevel.h │ ├── file.h │ ├── hash.h │ ├── valgrind.h │ ├── talloc.h │ ├── calls.h │ ├── includeSingeli.h │ ├── hash.c │ ├── utf.c │ └── valgrind.c ├── windows │ ├── getline.h │ ├── realpath.c │ ├── winError.h │ ├── getline.c │ ├── utf16.h │ └── sh.h ├── jit │ ├── nvm_placeholder.c │ └── nvm.c ├── opt │ ├── gc.h │ ├── mm_malloc.c │ ├── mm_malloc.h │ ├── mm_2buddy.h │ ├── mm_buddyTemplate.h │ ├── mm_buddy.h │ ├── mm_2buddy.c │ └── single.c ├── core │ ├── mm.c │ ├── gstack.h │ ├── tyarrTemplate.h │ ├── chrarr.h │ ├── derv.h │ ├── tyarrTemplate.c │ ├── heap.c │ ├── harr.c │ ├── tyarr.c │ ├── fillarr.h │ ├── numarr.h │ └── arrFns.h ├── nfns.h ├── core.h ├── load.h ├── nfns.c ├── builtins │ ├── inverse.c │ ├── sort.c │ └── radix.h └── ns.h ├── docs ├── README.md └── commands.md ├── .gitmodules ├── .gitignore └── licenses ├── LICENSE-Boost └── LICENSE-MIT-sort /test/ffi/shared.expected: -------------------------------------------------------------------------------- 1 | 4 2 | '!' 3 | -------------------------------------------------------------------------------- /test/generated/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /test/imports/cyclic.bqn: -------------------------------------------------------------------------------- 1 | •Import"cyclic.bqn" -------------------------------------------------------------------------------- /test/imports/cyclic_A.bqn: -------------------------------------------------------------------------------- 1 | •Import"cyclic_B.bqn" -------------------------------------------------------------------------------- /test/imports/cyclic_B.bqn: -------------------------------------------------------------------------------- 1 | •Import"cyclic_A.bqn" -------------------------------------------------------------------------------- /test/imports/show.bqn: -------------------------------------------------------------------------------- 1 | ⟨{⇐}, +, "show.bqn", •args⟩ 2 | -------------------------------------------------------------------------------- /test/imports/notCyclic.bqn: -------------------------------------------------------------------------------- 1 | {•args≡0? 0; 10+(•args-1) •Import "notCyclic.bqn"} -------------------------------------------------------------------------------- /src/singeli/src/clmul.singeli: -------------------------------------------------------------------------------- 1 | def clmul{a:T, b:T, imm if w128i{T}} = emit{T, '_mm_clmulepi64_si128', a, b, imm} 2 | def clmul{a, b} = clmul{a, b, 0} 3 | -------------------------------------------------------------------------------- /src/utils/mem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | ux getPageSize(void); 3 | #define ALLOC_PADDING 1024 // number of accessible padding bytes always required around an object 4 | -------------------------------------------------------------------------------- /test/cases/fuzz/join.bqn: -------------------------------------------------------------------------------- 1 | %DEF rand r ← (⊑•args).GetRand@ 2 | 3 | ( 4 | %USE rand 5 | # ∾𝕩 not getting garbage 6 | ≠{𝕊: ! ∧´1=⥊∾⎊1 ⥊⟜1¨ (4 r.Range 3) r.Range¨ 4}¨↕10000 7 | ) 8 | -------------------------------------------------------------------------------- /src/windows/getline.h: -------------------------------------------------------------------------------- 1 | #ifndef GETLINE_H 2 | #define GETLINE_H 3 | 4 | #include 5 | #include 6 | 7 | #define MAX_LINE_LENGTH 8192 8 | 9 | ssize_t getline(char **lptr, size_t *n, FILE *fp); 10 | 11 | #endif /* GETLINE_H */ -------------------------------------------------------------------------------- /src/singeli/src/debug.singeli: -------------------------------------------------------------------------------- 1 | show{'debug.singeli in use'} 2 | include 'debug/printf' # printf & lprintf 3 | 4 | def assert{x:(u1)} = { if (not x) emit{void, '__builtin_trap'} } 5 | def test_assert = assert # test_assert is guaranteed to either not exist, or always trap on bad input -------------------------------------------------------------------------------- /test/cases/fuzz/shift.bqn: -------------------------------------------------------------------------------- 1 | ( 2 | # bitarr »˘ / «˘ 3 | r ← (⊑•args).GetRand@ 4 | _test ← {𝔽_𝕣𝕩: 5 | a ← (1+2 r.Range 100) r.Range 2 6 | v ← r.Range 2 7 | v‿a ! (v 𝔽○•internal.Keep˘ a) ≡ v 𝔽˘ a 8 | } 9 | » _test¨ ↕10000 10 | « _test¨ ↕10000 11 | ) 12 | -------------------------------------------------------------------------------- /test/cases/fuzz/insert.bqn: -------------------------------------------------------------------------------- 1 | %DEF rand r ← (⊑•args).GetRand@ 2 | 3 | %USE rand ⋄ ≢{! (+˝ ≡ +○•internal.Keep˝) (1‿0+𝕩)r.Range 0}¨ ↕100‿100 4 | %USE rand ⋄ ≢{! (⌊˝ ≡ ⌊○•internal.Keep˝) (1‿0+𝕩)r.Range 0}¨ ↕100‿100 5 | %USE rand ⋄ ≢{! (⌈˝ ≡ ⌈○•internal.Keep˝) (1‿0+𝕩)r.Range 0}¨ ↕100‿100 6 | -------------------------------------------------------------------------------- /src/jit/nvm_placeholder.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #include "../vm.h" 3 | B evalJIT(Body* b, Scope* sc, u8* ptr) { thrM("JIT: Not supported"); } 4 | Nvm_res m_nvm(Body* b) { thrM("JIT: Not supported"); } 5 | void nvm_free(u8* ptr) { thrM("JIT: Not supported"); } 6 | void mmX_dumpHeap(FILE* f) { } 7 | -------------------------------------------------------------------------------- /src/utils/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | static inline u64 nsTime() { 5 | struct timespec t; 6 | // timespec_get(&t, TIME_UTC); // doesn't seem to exist on Android 7 | clock_gettime(CLOCK_REALTIME, &t); 8 | return (u64)(t.tv_sec*1000000000ll + t.tv_nsec); 9 | } 10 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # CBQN-specific documentation 2 | 3 | Only things specific to the CBQN implementation are here; see [the BQN documentation](https://mlochbaum.github.io/BQN/doc/index.html) for implementation-agnostic information. 4 | 5 | * [system functions](system.md) 6 | * [system commands](commands.md) -------------------------------------------------------------------------------- /src/windows/realpath.c: -------------------------------------------------------------------------------- 1 | WCHAR* realpath (const WCHAR*__restrict path, WCHAR*__restrict resolved_path) { 2 | return _wfullpath(NULL, path, 0); 3 | } 4 | bool winIsAbsolute(const char* path) { // TODO something more proper 5 | return *path && path[1]==':' && (!path[2] || path[2]=='/' || path[2]=='\\'); 6 | } 7 | -------------------------------------------------------------------------------- /src/singeli/src/dyarith.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | include './cbqnDefs' 3 | include './f64' 4 | include './bitops' 5 | include './mask' 6 | include './dyarith_common' 7 | 8 | def bqn_or{a, b} = (a+b)-(a*b) 9 | 10 | export{'orSAc_f64_f64_f64', arith_sa{2,bqn_or,0,f64,f64,f64}} 11 | include 'gen/arDefs' 12 | -------------------------------------------------------------------------------- /src/utils/cstr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "talloc.h" 3 | #include "utf.h" 4 | 5 | static char* toCStr(B x) { // doesn't consume 6 | u64 len = utf8lenB(x); 7 | TALLOC(char, p, len+1); 8 | toUTF8(x, p); 9 | p[len] = 0; 10 | return p; 11 | } 12 | static void freeCStr(char* p) { 13 | TFREE(p); 14 | } 15 | -------------------------------------------------------------------------------- /test/ffi/libTest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../../include/bqnffi.h" 4 | 5 | int main() { 6 | bqn_init(); 7 | double res = bqn_toF64(bqn_evalCStr("2+2")); 8 | printf("%g\n", res); 9 | bqn_free(bqn_evalCStr("f←@ •FFI \"a\"‿\"bqn_makeChar\"‿\">u64\" ⋄ •Show F 33")); 10 | } 11 | -------------------------------------------------------------------------------- /test/cases/fuzz/member-of.bqn: -------------------------------------------------------------------------------- 1 | ( 2 | # character 𝕨∊𝕩 3 | ⟨V⇐Variation,LV⇐ListVariations,CL⇐ClearRefs,EE⇐EEqual⟩←•internal 4 | R ← ((⊑•args).GetRand@).Range 5 | _t←{w F _𝕣 x: ∧´EE¨⟜⊏⥊(LV 𝕨){(𝕨 V w) F 𝕩 V x}⌜LV𝕩?CL@; •Show¨𝕗‿𝕨‿𝕩⋄!0} 6 | GS←{𝕊: @+0⌈(¯1+2⋆R∘≠⊸⊑7‿8‿15‿16‿20) - R˜R 200} 7 | {𝕊:(GS@) ∊_t GS@}¨↕1000 8 | ) 9 | -------------------------------------------------------------------------------- /test/cases/fuzz/group.bqn: -------------------------------------------------------------------------------- 1 | %DEF eqvar ⟨_eqvar⟩←⊑•args 2 | 3 | ( 4 | %USE eqvar 5 | helpers ← ⊑•args 6 | rand ← helpers.GetRand@ 7 | R ← rand.Range 8 | ⟨RandVals⟩ ← ⟨rand⟩ helpers.Import "utils.bqn" 9 | {𝕊: 10 | t ← R 8 11 | l ← R 10 12 | x ← l RandVals t 13 | w ← ¯1+l R 1+l 14 | w ⊔_eqvar x 15 | }¨ ↕10000 16 | ) -------------------------------------------------------------------------------- /src/jit/nvm.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #include "../vm.h" 3 | 4 | u64 mm_heapUsed(); 5 | #if JIT_START!=-1 6 | #include "nvm_x86_64.c" 7 | u64 tot_heapUsed() { 8 | return mm_heapUsed() + mmX_heapUsed(); 9 | } 10 | #else 11 | #include "nvm_placeholder.c" 12 | u64 tot_heapUsed() { 13 | return mm_heapUsed(); 14 | } 15 | #endif 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "build/singeliSubmodule"] 2 | path = build/singeliSubmodule 3 | url = https://github.com/mlochbaum/Singeli 4 | [submodule "build/replxxSubmodule"] 5 | path = build/replxxSubmodule 6 | url = https://github.com/dzaima/replxx 7 | [submodule "build/bytecodeSubmodule"] 8 | path = build/bytecodeSubmodule 9 | url = https://github.com/dzaima/cbqnBytecode 10 | -------------------------------------------------------------------------------- /test/cases/unaligned.bqn: -------------------------------------------------------------------------------- 1 | ⊑ 8‿32•bit._cast 3↓↕7 %% 100992003 2 | 3 | ( 4 | r←(⊑•args).GetRand@ 5 | { ·‿dw‿·𝕊sk‿sw‿e: 6 | a0 ← { 7 | dw=64? 𝕩⥊0‿1; 8 | 𝕩 r.Range e 9 | } (1+8×1⌈dw÷sw) 10 | {•internal.Keep¨ sw‿dw •bit._cast 1↓𝕩•internal.Variation a0}¨ ∾⟜"Inc"¨⊸∾ ∾⟜sk¨ "AS" 11 | }⌜˜ ⟨"b"‿1‿2, "i8"‿8‿100, "i16"‿16‿1e4, "i32"‿32‿1e9, "f64"‿64‿0⟩ 12 | ) 13 | -------------------------------------------------------------------------------- /test/cases/usz32.bqn: -------------------------------------------------------------------------------- 1 | !"Out of memory" % a←(2⋆20) ⥊ 1e9 ⋄ ≠•internal.Keep {𝕊: a}˘ a 2 | !"𝕨⥊𝕩: 𝕨 too large" % a←(2⋆20) ⥊ 1e9 ⋄ ≠•internal.Keep a˘ a 3 | !"Out of memory" % ≠•internal.Keep (2⋆25)/(2⋆10)⥊10 4 | !"Out of memory" % k←16384 ⋄ n←k÷˜2⋆32 ⋄ ≠•internal.Keep (n⥊k) / n⥊1 5 | !"Out of memory" % k←64 ⋄ n←k÷˜2⋆32 ⋄ ≠•internal.Keep (n⥊k) / n⥊1 # %SLOW 6 | !"Out of memory" % ≠•internal.Keep ∾˜⍟31 ↕2 # %SLOW 7 | -------------------------------------------------------------------------------- /src/utils/interrupt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if REPL_INTERRUPT && !__has_include() 3 | #undef REPL_INTERRUPT 4 | #endif 5 | 6 | bool cbqn_takeInterrupts(bool b); 7 | 8 | #if REPL_INTERRUPT 9 | extern volatile int cbqn_interrupted; 10 | NOINLINE NORETURN void cbqn_onInterrupt(); 11 | #define CHECK_INTERRUPT ({ if (cbqn_interrupted) cbqn_onInterrupt(); }) 12 | #else 13 | #define CHECK_INTERRUPT 14 | #endif -------------------------------------------------------------------------------- /src/singeli/src/spaced.singeli: -------------------------------------------------------------------------------- 1 | def spaced_mask_of{l} = emit{u64, 'get_spaced_mask', l} # see slash.singeli 2 | 3 | def aligned_spaced_mask{l} = { 4 | assert{l <= 64} 5 | assert{is_pow2{l}} 6 | spaced_mask_of{l} 7 | } 8 | 9 | def unaligned_spaced_mask_mod{l:T} = { 10 | assert{l < 64} 11 | def m = spaced_mask_of{l} 12 | def d = cast_i{T, ctz{m}} # = 64%l 13 | tup{m>>d | m<<(l-d), d} 14 | } 15 | 16 | def advance_spaced_mask{k, m, sh} = m<<(k-sh) | m>>sh 17 | -------------------------------------------------------------------------------- /test/mainCfgs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | if [ "$#" -ne 1 ]; then 3 | echo "Usage: $0 path/to/mlochbaum/BQN" 4 | exit 5 | fi 6 | make heapverify && echo 'heapverify:' && ./BQN -M 1000 "$1/test/this.bqn" -noerr bytecode header identity literal namespace prim simple syntax token under undo unhead || exit 7 | make rtverify && echo 'rtverify:' && ./BQN -M 1000 "$1/test/this.bqn" || exit 8 | make CC=gcc c && echo 'gcc:' && ./BQN -M 1000 "$1/test/this.bqn" || exit 9 | 10 | -------------------------------------------------------------------------------- /src/opt/gc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern GLOBAL u64 gc_depth; 4 | extern GLOBAL bool gc_running; 5 | static void gc_disable() { gc_depth++; } 6 | static void gc_enable() { gc_depth--; } 7 | 8 | #if GC_LOG_DETAILED 9 | extern u64 gcs_visitBytes, gcs_visitCount, gcs_freedBytes, gcs_freedCount; 10 | #endif 11 | 12 | extern void gc_onVisit(Value*); 13 | static void mm_visit(B x) { 14 | if (isVal(x)) gc_onVisit(v(x)); 15 | } 16 | static void mm_visitP(void* xp) { 17 | gc_onVisit(xp); 18 | } 19 | -------------------------------------------------------------------------------- /test/cases/fuzz/bitarr-fill.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | helpers ← ⊑•args 3 | Range ← (helpers.GetRand@).Range 4 | 5 | Do ← {𝕊: 6 | orig ← (1000⌊𝕩) Range 2 7 | s ← Range 1+≠orig 8 | mut ← "Ab"•internal.Variation orig 9 | l ← Range (1+≠orig)-s 10 | v ← Range 2 11 | 201 •internal.Temp ⟨0,s,0,l,mut⟩ 12 | exp ← 0¨⌾((s+↕l)⊸⊏) orig 13 | mut ≢ exp? 14 | •Show '0'+orig 15 | •Show '0'+mut 16 | •Show '0'+exp 17 | •Show s‿l‿v 18 | !0; 19 | 𝕩 20 | } 21 | 22 | Do¨ ↕100000 -------------------------------------------------------------------------------- /src/utils/each.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | B eachd_fn(B fo, B w, B x, FC2 f); // consumes w,x; assumes at least one is array 4 | B eachm_fn(B fo, B x, FC1 f); // consumes x; x must be array 5 | 6 | static inline B squeezed_unit(B x) { // same fill as squeeze_any(m_hunit(x)) 7 | return isAtm(x)? m_unit(x) : m_hunit(x); 8 | } 9 | 10 | #if SEMANTIC_CATCH 11 | NOINLINE B arith_recd(FC2 f, B w, B x); 12 | #else 13 | static inline B arith_recd(FC2 f, B w, B x) { return eachd_fn(bi_N, w, x, f); } 14 | #endif 15 | -------------------------------------------------------------------------------- /src/opt/mm_malloc.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #include 3 | #include 4 | 5 | #if USE_VALGRIND 6 | #warning "USE_VALGRIND=1 and MM=0 don't work well together; CBQN requires the ability to read past the end of allocations, but malloc doesn't provide that." 7 | #endif 8 | 9 | void gc_add(B x) { } 10 | void gc_addFn(vfn f) { } 11 | void gc_add_ref(B* x) { } 12 | bool gc_maybeGC(bool toplevel) { return false; } 13 | void gc_forceGC(bool toplevel) { } 14 | void mm_forHeap(V2v f) { } 15 | u64 mm_heapUsed() { return 123; } // idk 16 | void mm_dumpHeap(FILE* f) { } 17 | -------------------------------------------------------------------------------- /src/singeli/src/monarith.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | include './cbqnDefs' 3 | include './f64' 4 | include './bitops' 5 | include './mask' 6 | 7 | 8 | fn absFn{T}(r:*void, x:*void, len:u64) : u64 = { 9 | def bulk = arch_defvw{}/width{T} 10 | def VT = [bulk]T 11 | @for_mu{bulk, tern{T==f64, 2, 1}}(cx in tup{VT,*T~~x}, sr in tup{'g',*T~~r}, M in 'm' over is to len) { 12 | if (T!=f64 and any_hom{M, ...eachx{==, cx, VT**minvalue{T}}}) return{select{is,0}*bulk} 13 | sr{each{__abs, cx}} 14 | } 15 | len 16 | } 17 | 18 | export_tab{'simd_abs', each{absFn, tup{i8, i16, i32, f64}}} 19 | -------------------------------------------------------------------------------- /src/singeli/src/f64.singeli: -------------------------------------------------------------------------------- 1 | def __ceil{x:(f64)} = emit{f64, 'ceil', x} 2 | def __floor{x:(f64)} = emit{f64, 'floor', x} 3 | def __abs{x:(f64)} = emit{f64, 'fabs', x} 4 | 5 | def NaN = 0.0/0.0 6 | def isNaN{x:(f64)} = x!=x 7 | def qNaN{x:(u64)} = (x<<1) == (cast{u64, 0x8ff8} << 49) 8 | 9 | def ftrunc{T== i8, x:(f64)} = emit{T, '', x} 10 | def ftrunc{T==i16, x:(f64)} = emit{T, '', x} 11 | def ftrunc{T==i32, x:(f64)} = emit{T, '', x} # maybe explicitly use _mm_cvtsd_si32? 12 | def ftrunc{T==i64, x:(f64)} = emit{T, '', x} 13 | def fext{x} = emit{f64, '', x} 14 | 15 | def interp_f64{x:(u64)} = emit{f64, 'interp_f64', x} 16 | -------------------------------------------------------------------------------- /test/cases/fuzz/arith.bqn: -------------------------------------------------------------------------------- 1 | ( 2 | helpers ← ⊑•args 3 | R ← (helpers.GetRand@).Range 4 | ⟨EEqual, Variation⟩ ⇐ •internal 5 | 6 | Mod ← {𝕊: 7 | modOptions ← ⟨∞,¯∞,0÷0⟩ ∾ ∾⟜- 2⋆↕34 8 | {𝕊: 9 | n ← R 2⋆4+R 6 10 | w ← R∘≠⊸⊑ modOptions 11 | x ← -⍟(R 2) (2⋆R 34) + (↕n)-R n 12 | got ← w|x 13 | exp ← w|"Ah" Variation x 14 | ¬exp EEqual got? 15 | •Out "𝕨: "∾•Repr w 16 | •Out "𝕩: "∾(•Repr x)∾" / "∾•internal.Info x 17 | •Out "exp: "∾•Repr exp 18 | •Out "got: "∾•Repr got 19 | !0 20 | ;@ 21 | }¨ ↕𝕩 22 | } 23 | 24 | •Show "powerOfTwo | arr" ⋄ Mod 100000 25 | ) -------------------------------------------------------------------------------- /src/singeli/src/sort.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | include './cbqnDefs' 3 | include './mask' 4 | 5 | fn is_sorted{E}(x:*void, down:u64, n:usz) : u1 = { # TODO: both down and n should be ux when Singeli can handle that 6 | assert{n > 0} 7 | def x = *E~~x 8 | def a = x + down 9 | def b = x + 1 - down 10 | def bulk = arch_defvw{} / width{E} 11 | def V = [bulk]E 12 | @for_mu{bulk, 1}(a in tup{V,a}, b in tup{V,b}, M in 'm' over n) { 13 | if (any_hom{M, ...each{tot_gt, a, b}}) return{0} 14 | } 15 | 1 16 | } 17 | 18 | export_tab{'si_is_sorted', each{is_sorted, tup{ 19 | i8,i16,i32,f64, 20 | u8,u16,i32 # reuse i32 for 32-bit char 21 | }}} 22 | -------------------------------------------------------------------------------- /src/utils/utf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void fprintCodepoint(FILE* f, u32 v); // single codepoint 4 | void fprintsUTF8(FILE* f, char* s); // UTF-8, null-terminated 5 | void fprintsU32(FILE* f, u32* s, usz len); // unicode codepoints 6 | void fprintsB(FILE* f, B x); // codepoints of x; doesn't consume; assumes argument is an array, doesn't care about rank, throws if its elements are not characters 7 | 8 | u64 utf8lenB(B x); // doesn't consume; may error as it verifies whether is all chars 9 | void toUTF8(B x, char* p); // doesn't consume; doesn't verify anything; p must have utf8lenB(x) bytes (calculating which should verify that this call is ok), add trailing null yourself 10 | -------------------------------------------------------------------------------- /test/cases/fuzz/scan.bqn: -------------------------------------------------------------------------------- 1 | # tests scan on a matrix checking for overflow in tail elements 2 | 8‿16‿32 {e𝕊n‿w: m←2⋆e-2 ⋄ ! (m⊸+⌾(⟨¯1,w|n⟩⊸⊑) ⌽‿w⥊(n+w+1)⥊m) ≡ +` •internal.Squeeze ↑‿w⥊ (w⥊m)∾(n⥊0)∾m}⌜ 1+↕200‿65 3 | 4 | %DEF rand R ← ((⊑•args).GetRand@).Range ⋄ ⟨V⇐Variation⟩ ← •internal 5 | 6 | # ∨`bitarr 7 | ( 8 | %USE rand 9 | {𝕊: 10 | n ← 1 + R 2⋆R 13 11 | ! (∨` ≡ ⊢∘∨`) "Ab" V {𝕊: 1⌾((R n)⊸⊑)𝕩}⍟2 "Ai8" V 0⥊˜ n 12 | }¨ ↕100000 13 | ) 14 | 15 | # +`bitarr 16 | ( 17 | %USE rand 18 | RB2 ← {¬⍟(R 2) "Ab" V 𝕩 R 1+R 2⋆R 10} # random boolean array with random uniform probability 19 | {𝕊: 20 | a ← RB2 R 2⋆R 13 21 | ! (+` ≡ ⊢∘+`) a 22 | }¨ ↕100000 23 | ) 24 | -------------------------------------------------------------------------------- /src/core/mm.c: -------------------------------------------------------------------------------- 1 | #define MM_C 1 2 | #include "../core.h" 3 | #include "../utils/mem.h" 4 | 5 | static u64 prepAllocSize(u64 sz) { 6 | u64 psz = getPageSize(); 7 | u64 minTotPad = ALLOC_PADDING*2 + 128; 8 | if (psz < minTotPad) psz = minTotPad; 9 | return sz + psz; 10 | } 11 | #define MMAP(SZ) mmap(NULL, SZ, PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) 12 | 13 | GLOBAL bool mem_log_enabled; 14 | 15 | #if MM==0 16 | #include "../opt/mm_malloc.c" 17 | #elif MM==1 18 | #include "../opt/mm_buddy.c" 19 | #elif MM==2 20 | #include "../opt/mm_2buddy.c" 21 | #else 22 | #error "bad MM value" 23 | #endif 24 | #ifndef CLANGD 25 | #undef MMAP 26 | #endif 27 | -------------------------------------------------------------------------------- /test/ffi/makefile: -------------------------------------------------------------------------------- 1 | test: testLib testShared 2 | 3 | 4 | 5 | testShared: buildShared 6 | @LD_LIBRARY_PATH=../../ ./sharedTest > shared.got 7 | @diff --color -su shared.expected shared.got 8 | 9 | buildShared: 10 | mkdir -p sharedLibFolder && cp ../../libcbqn.so sharedLibFolder 11 | $(CC) -g libTest.c -o sharedTest -LsharedLibFolder -lcbqn 12 | 13 | buildStatic: 14 | mkdir -p staticLibFolder && cp ../../libcbqn.a staticLibFolder 15 | $(CC) -g libTest.c -o staticTest -LstaticLibFolder -lcbqn -lm -lffi 16 | 17 | 18 | 19 | testLib: buildLib 20 | @../../BQN test.bqn > test.got 21 | @diff --color -su test.expected test.got 22 | 23 | buildLib: 24 | $(CC) -O3 -g -c -fpic ffiTest.c -o ffiTest.o 25 | $(CC) -shared -olib.so ffiTest.o -------------------------------------------------------------------------------- /src/singeli/src/bmi.singeli: -------------------------------------------------------------------------------- 1 | def x86_tzcnt{x:T if (T==u16 or T==u32 or T==u64) and hasarch{'BMI' }} = emit{T, merge{'_tzcnt_u',fmtnat{width{T}}}, x} 2 | def x86_lzcnt{x:T if ( T==u32 or T==u64) and hasarch{'LZCNT'}} = emit{T, merge{'_lzcnt_u',fmtnat{width{T}}}, x} 3 | 4 | def pdep{x:T, m:T==u64 if hasarch{'BMI2'}} = emit{T, '_pdep_u64', x, m} 5 | def pdep{x:T, m:T==u32 if hasarch{'BMI2'}} = emit{T, '_pdep_u32', x, m} 6 | def pext{x:T, m:T==u64 if hasarch{'BMI2'}} = emit{T, '_pext_u64', x, m} 7 | def pext{x:T, m:T==u32 if hasarch{'BMI2'}} = emit{T, '_pext_u32', x, m} 8 | 9 | # only low 8 bits of n matter, so any int is fine 10 | def bzhi{x:T==u64, n if any_int{n} and hasarch{'BMI2'}} = emit{T, '_bzhi_u64', x, n} 11 | def bzhi{x:T==u32, n if any_int{n} and hasarch{'BMI2'}} = emit{T, '_bzhi_u32', x, n} 12 | -------------------------------------------------------------------------------- /src/windows/winError.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // memory is allocated and should be freed with LocalFree() 4 | // for now this memory is leaked 5 | static char* winErrorEx(DWORD dwError) { 6 | char* buffer = NULL; 7 | DWORD dwFlags = FORMAT_MESSAGE_MAX_WIDTH_MASK // no newlines 8 | | FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate memory 9 | | FORMAT_MESSAGE_FROM_SYSTEM // get error message 10 | | FORMAT_MESSAGE_IGNORE_INSERTS; // no argument (NULL) 11 | DWORD dwResult = FormatMessageA(dwFlags, NULL, dwError, 0, (LPSTR)&buffer, 0, NULL); 12 | if (dwResult==0 || buffer==NULL) { 13 | fatal("Failed to get error message from FormatMessageA()"); 14 | } 15 | return buffer; 16 | } 17 | 18 | static char* winError(void) { return winErrorEx(GetLastError()); } 19 | -------------------------------------------------------------------------------- /test/cases/fuzz/hash.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # note: tests only bit/i8/i16/i32 arrays; no f64/c8/c16/c32/generic array tests 3 | helpers ← ⊑•args 4 | R ← (helpers.GetRand@).Range 5 | 6 | M ← (2⋆32)⊸×⊸+´ 7 | Test ← { 𝕊: 8 | width ← 1‿8‿16‿32⊑˜R 4 9 | count ← 1+R (width=1)⊑32‿256 10 | data ← (width×count) R 2 11 | n ← 1+R count 12 | cast ← {width=1? ⊢; ⟨1⋄width‿'i'⟩ •bit._cast} 13 | iota ← ↕≠data 14 | shifts ← ↕1+count-n 15 | exp ← >(-shifts)(width/⌽)¨↕count 16 | got ← >1↓¨∊¨<˘⍉>{ 17 | a ← Cast data≠𝕩≠iota 18 | shifts (M∘•Hash n↑↓)¨ ⋈<)got 21 | } 22 | 23 | fails ← 0‿0 24 | {𝕊: fails+↩ Test@}¨↕1000 25 | •Out ∾⟨"Collisions: " ⋄ •Repr 0⊑fails ⋄ " (expected 0, assuming no actual hash collisions)"⟩ 26 | •Out ∾⟨"Mismatches: " ⋄ •Repr 1⊑fails ⋄ " (expected 0)"⟩ 27 | ! 0 = 1⊑fails 28 | -------------------------------------------------------------------------------- /test/cases/gc.bqn: -------------------------------------------------------------------------------- 1 | %DEF base ( 2 | ⟨V⇐Variation, U⇐Unshare, LV⇐ListVariations ⋄ CLR⇐ClearRefs, GC⟩ ← •internal 3 | UseGC ← {𝕊: 𝕨 ⋄ 𝕩 ⋄ GC@ ⋄ @} 4 | _tvaru ← { 5 | F _𝕣 x: { F 𝕩 V U¨ x ⋄ CLR@}¨ LV 𝕩; 6 | w F _𝕣 x: (LV 𝕨) {(𝕨 V U¨ w) F 𝕩 V U¨ x ⋄ CLR@}⌜ LV 𝕩 7 | } 8 | ) 9 | 10 | %USE base ⋄ UseGC¨_tvaru ↕⋈4 11 | %USE base ⋄ UseGC¨_tvaru˜ ↕⋈4 12 | %USE base ⋄ (↕4)⊸(UseGC¨)_tvaru ↕⋈4 13 | %USE base ⋄ (UseGC¨)⟜(↕4)_tvaru ↕⋈4 14 | 15 | %USE base ⋄ UseGC` _tvaru ↕⋈5 16 | %USE base ⋄ 1⊸(UseGC`)_tvaru ↕⋈5 17 | %USE base ⋄ UseGC`_tvaru ↕4‿2 18 | %USE base ⋄ (↕⋈2) UseGC`_tvaru ↕4‿2 19 | 20 | %USE base ⋄ UseGC´ _tvaru ↕⋈4 21 | %USE base ⋄ @⊸(UseGC´)_tvaru ↕⋈4 22 | 23 | %USE base ⋄ UseGC˝ _tvaru ↕⋈4 24 | %USE base ⋄ @⊸(UseGC˝)_tvaru ↕⋈4 25 | %USE base ⋄ UseGC˝ _tvaru ↕4‿2 26 | %USE base ⋄ (↕⋈2) (UseGC˝)_tvaru ↕4‿2 27 | -------------------------------------------------------------------------------- /test/joinReuse.bqn: -------------------------------------------------------------------------------- 1 | ⟨V⇐Variation ⋄ LV⇐ListVariations⟩←•internal 2 | AllEq ← !¨ (<∘⊑≡¨⊢) 3 | # all that's important in the output is that there are at least some "reuse:1"s and "reuse:0"s for each item; quoted errors in the output are expected 4 | 5 | { w𝕊x: 6 | •Out "next" 7 | AllEq t ← {(⥊ ⋈ ≢) (𝕨 V w)∾𝕩 V x}⌜´ LV¨ 𝕨‿𝕩 8 | •Out "" ⋄ •Show ⊑t 9 | }´¨ ⟨ 10 | "ab"‿["01","23"] 11 | ["ab","cd"]‿"01" 12 | ["ab","cd","ef"]‿["01","23"] 13 | [["ab","cd"]⋄["ef","gh"]⋄["ij","kl"]]‿["01","23"] 14 | ⟩ 15 | 16 | {𝕊: 17 | 18 | { w𝕊x: 19 | •Out "next" 20 | AllEq t ← {(𝕨 V w)∾𝕩 V x ⋄ •Out "Expected error, didn't get one; fail" ⋄ •Exit 1}⎊{𝕊:•CurrentError@}⌜´ LV¨ 𝕨‿𝕩 21 | •Out "" ⋄ •Show ⊑t 22 | }´¨ ⟨ 23 | "abc"‿["01","23"] 24 | ["ab","cd"]‿"012" 25 | ["ab","cd","ef"]‿["012","345"] 26 | [["ab","cd"]⋄["ef","gh"]⋄["ij","kl"]]‿["012","345"] 27 | ⟩ 28 | }⍟⊢ 0=≠•args -------------------------------------------------------------------------------- /test/x86Cfgs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | if [ "$#" -ne 1 ]; then 3 | echo "Usage: $0 path/to/mlochbaum/BQN" 4 | exit 5 | fi 6 | make f='-DDEBUG -DHEAP_VERIFY -DUSE_SETJMP=0 -DJIT_START=0' single-c 7 | echo 'alljit+heapverify:' && ./BQN -M 1000 "$1/test/this.bqn" -noerr bytecode header identity literal namespace prim simple syntax token under undo unhead || exit 8 | echo 'native vfy:';make heapverifyn && ./BQN -M 1000 "$1/test/this.bqn" -noerr bytecode header identity literal namespace prim simple syntax token under undo unhead || exit 9 | echo 'native:';make o3n && ./BQN -M 1000 "$1/test/this.bqn" || exit 10 | echo 'sse2:';build/build singeli native=0 && ./BQN -M 1000 "$1/test/this.bqn" || exit 11 | echo '32-bit:';make f='-DDEBUG -m32' single-c FFI=0 && ./BQN -M 1000 "$1/test/this.bqn" || exit 12 | echo '(expected one failed test: 0.3‿1.01‿π(⋆⁼⌜≡÷˜⌜○(⋆⁼))0‿2‿5.6‿∞)' 13 | -------------------------------------------------------------------------------- /test/cases/build-specific/test_range.bqn: -------------------------------------------------------------------------------- 1 | %DEF rand r ← (⊑•args).GetRand@ 2 | 3 | 203 •internal.Temp 3‿1‿4‿1‿5‿9 %% 1‿9‿1 4 | 5 | %DEF invalid Invalid ← {·‿·‿0: 1; s‿e‿1: (s<-2⋆52) ∨ e>2⋆52} 203•internal.Temp⊢ 6 | 7 | ≢(⟨⟨0‿9‿1,↕10⟩ ⋄ ⟨¯5‿¯3‿1 ⋄ ¯5‿¯3‿¯4‿¯4‿¯4‿¯4‿¯4⟩⟩∾{⟨⟨¯2,𝕩-3,1⟩,¯2+↕𝕩⟩}¨1+↕100) {exp‿a𝕊𝕩: exp (⋈!≡) 203•internal.Temp 𝕩•internal.Variation a}⌜ "Ai8"‿"Ai16"‿"Ai32"‿"Af64" 8 | %USE rand ⋄ T←⋈ ! ⊣ ≡ 203•internal.Temp "Ab"•internal.Variation⊢ ⋄ ≠{𝕊n: 1‿1‿1 T 𝕩⥊1 ⋄ 0‿0‿1 T 𝕩⥊0 ⋄ 0‿1 {0‿1‿1 T (¬𝕨)⌾(𝕩⊸⊑) n⥊𝕨}¨⍟(n>1) 2 r.Range n}¨1+↕500 9 | %USE invalid ⋄ Invalid¨ ⟨↕4, -⊸∾ 2⋆↕50, π×↕4, 10⥊2⋆60⟩ %% 0‿0‿1‿1 10 | %USE invalid ⋄ ⟨2⋆63, 1.5, 0÷0⟩ {𝕨!Invalid 𝕨⌾(𝕩⊸⊑) 20⥊0}⌜ ↕20 11 | %USE invalid ⋄ p←⟨1+2⋆-52, 1-2⋆-53⟩ ⋄ {𝕩!Invalid 𝕩⌾(10⊸⊑) 20⥊0}¨ ⟨∞, ¯∞, 0.1, ¯0.1⟩ ∾ ((1∾p) ×⌜ -⊸∾ 2⋆54+↕12) ∾○⥊ p ×⌜ 2⋆↕70 12 | ≠{! 0‿0‿1 ≡ 203•internal.Temp 𝕩↑(𝕩⥊0)∾10⥊0÷0}¨ 1+↕10 13 | {𝕩!0= 2⊑ 203•internal.Temp (0÷0)¨⌾((⍷𝕩)⊸⊏) 10⥊0}¨ ↕10‿10‿10 14 | -------------------------------------------------------------------------------- /test/cases/fuzz/bitcpy.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # tests bit_cpy 3 | r ← (⊑•args).GetRand@ 4 | ⟨Temp, Type, Unshare, Squeeze, Info, Variation⟩ ← •internal 5 | 6 | Do ← { 𝕊: 7 | al ← 1+r.Range 500 8 | a ← al r.Range 2 9 | aT ← "Ai32" Variation a 10 | ! a ≡ aT 11 | ! "bitarr" ≡ Type a 12 | b ← al r.Range 2 13 | ! "bitarr" ≡ Type b 14 | { 𝕊: 15 | bl ← r.Range 1+al 16 | as ← r.Range 1+al-bl 17 | bs ← r.Range 1+al-bl 18 | i ← ↕bl 19 | exp ← ((bs+i)⊏b)⌾((as+i)⊸⊏) a 20 | aC ← Unshare a 21 | 202 Temp ⟨aC, as, b, bs, bl⟩ 22 | aC ≢ exp? 23 | •Out "Fail:" 24 | F ← {(¬ ·∧`⌾⌽ ' '⊸=)⊸/ ⥊' '∾˘˜ ↑‿64⥊ '0'+𝕩} 25 | •Out "Exp: "∾F exp 26 | •Out "Got: "∾F aC 27 | •Out "Inputs:" 28 | •Out "r: "∾F a 29 | •Out "rs: "∾•Repr as 30 | •Out "x: "∾F b 31 | •Out "xs: "∾•Repr bs 32 | •Out "l: "∾•Repr bl 33 | !0 34 | ;0 35 | }∘@⍟500 @ 36 | ! a ≡ aT 37 | } 38 | 39 | @∘Do⍟2000 @ 40 | -------------------------------------------------------------------------------- /test/cases/fuzz/equal.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # tests 𝕨 ≡ 𝕩 3 | helpers ← ⊑•args 4 | r ← helpers.GetRand@ 5 | ⟨ListVariations, ClearRefs, Variation, Squeeze, Info⟩ ← •internal 6 | 7 | TestVars ← { w𝕊a‿b: 8 | n ← ¬w 9 | (ListVariations a) { 10 | ¬⍟n (𝕨 Variation a) ≡ (𝕩 Variation b)? @; 11 | •Out ∾"fail for "‿𝕨‿"/"‿𝕩‿", expected "‿(w⊑"0"‿"1")‿":" 12 | •Out •Repr a 13 | •Out •Repr b 14 | •Out •Repr a≠b 15 | !0 16 | }⌜ ListVariations b 17 | ClearRefs@ 18 | } 19 | 20 | (1 TestVars ⋈˜)¨ ⟨⟨⟩ ⋄ "" ⋄ ↕0⟩ 21 | 22 | ⟨RandVals⟩ ← ⟨r⟩ helpers.Import "utils.bqn" 23 | 24 | Do ← { 𝕊: 25 | at ← r.Range 8 26 | l ← 1 + r.Range (at=0)⊑100‿700 27 | a ← l RandVals at 28 | (⊢◶1‿{𝕊:∧´a=a} at=4) TestVars a‿a 29 | { 𝕊: 30 | p ← r.Range l 31 | v ← ⊑1 RandVals ⊢◶⟨r.Range∘5 ⋄ 5+r.Range∘3⟩ at≥5 32 | exp ← v = p⊑a 33 | b ← Squeeze v⌾(p⊸⊑) a 34 | (⊢◶exp‿{𝕊:∧´a=b} at=4) TestVars a‿b 35 | }∘@⍟100 @ 36 | } 37 | 38 | @∘Do⍟1000 @ 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # outputs 2 | /BQN 3 | /BQN.html 4 | /BQN.js 5 | *.wasm 6 | *.cwasm 7 | libcbqn.so 8 | libcbqn.dylib 9 | cbqn.dll 10 | libcbqn.a 11 | *.exe 12 | *.dll 13 | 14 | # build system things 15 | /build/obj/ 16 | /build/obj2/ 17 | /build/singeliLocal 18 | /build/replxxLocal 19 | /build/bytecodeLocal 20 | /build/interp 21 | /src/singeli/gen 22 | 23 | # files CBQN may create 24 | /asm_* 25 | CBQNHeapDump 26 | .cbqn_repl_history 27 | cbqn_repl.txt 28 | cbqn-jit.bqn 29 | 30 | # test temporary files 31 | /test/ffi/ffiTest.o 32 | /test/ffi/test.got 33 | /test/ffi/shared.got 34 | /test/ffi/sharedTest 35 | /test/ffi/staticTest 36 | /test/ffi/sharedLibFolder 37 | /test/ffi/staticLibFolder 38 | /test/ffi/lib.so 39 | /test/testDir/ 40 | 41 | # other 42 | /local/ 43 | perf.data* 44 | 45 | # clangd / compile commands 46 | compile_commands.json 47 | .cache/ 48 | .singeli_compile_commands.json 49 | 50 | # things not used anymore, but still may be present in existing clones 51 | /SingeliClone/ 52 | /obj/ 53 | /src/gen/ -------------------------------------------------------------------------------- /src/nfns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "core.h" 3 | 4 | typedef struct NFnDesc NFnDesc; 5 | typedef struct NFn { // native function 6 | struct Fun; 7 | u32 id; // index in nfn_list 8 | // custom fields: 9 | i32 data; 10 | B obj; 11 | } NFn; 12 | struct NFnDesc { 13 | struct Value; 14 | u32 id; 15 | B name; 16 | FC1 c1; 17 | FC2 c2; 18 | }; 19 | 20 | NFnDesc* registerNFn(B name, FC1 c1, FC2 c2); // should be called a constant number of times; consumes name 21 | B m_nfn(NFnDesc* desc, B obj); // consumes obj 22 | B nfn_name(B x); // doesn't consume 23 | static void nfn_lateInit(NFn* fn, NFnDesc* desc) { 24 | fn->id = desc->id; 25 | } 26 | static B nfn_objU(B t) { 27 | assert(isVal(t) && TY(t) == t_nfn); 28 | return c(NFn,t)->obj; 29 | } 30 | static i32 nfn_data(B t) { 31 | assert(isVal(t) && TY(t) == t_nfn); 32 | return c(NFn,t)->data; 33 | } 34 | static B nfn_swapObj(B t, B n) { // consumes n, returns old value 35 | B p = c(NFn,t)->obj; 36 | c(NFn,t)->obj = n; 37 | return p; 38 | } -------------------------------------------------------------------------------- /test/precompiled.bqn: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env dbqn 2 | "Usage: ./precompiled.bqn path/to/mlochbaum/BQN ""$PATH"" [expressions, else prim tests]"!2≤≠•args 3 | path←0⊑•args 4 | envP←1⊑•args 5 | 6 | tests ← •FLines •wdpath•file.At path∾"/test/cases/prim.bqn" 7 | # tests ← •FLines path∾"/test/cases/identity.bqn" 8 | # tests ← •FLines path∾"/test/cases/undo.bqn" 9 | # tests ← •FLines path∾"/test/cases/under.bqn" 10 | # tests ← •FLines path∾"/test/cases/fill.bqn" 11 | {tests↩𝕩}⍟(×≠) 2↓•args 12 | 13 | ('#'≠ ·⊑ ∾⟜"#")◶@‿{ 14 | (⊑'%'⊸∊)◶{𝕤 15 | •Out 𝕩 16 | "../build/interp" •FChars ⟨1,path,𝕩⟩ •Import "../build/cc.bqn" 17 | # make ← "make"‿"singeli=1"‿"f=-DPRECOMP -march=native"‿"c" 18 | # make ← "make"‿"f=-DPRECOMP -m32"‿"lf=-m32"‿"c" 19 | make ← "make"‿"f=-DPRECOMP"‿"c" 20 | (×⊑)◶@‿{𝕤⋄•Out "############ Failed to compile! ############" ⋄ •Out¨1↓𝕩}{env⇐<"PATH="∾envP}•SH make 21 | code‿out‿err←•SH⟨•file.At "../BQN"⟩ 22 | •Out out 23 | {𝕤⋄•Out"exit code "∾(•Repr code) ⋄ •Out err}⍟(×code) err 24 | }‿⊢ 25 | }¨tests -------------------------------------------------------------------------------- /src/opt/mm_malloc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | extern GLOBAL u64 mm_heapAlloc; 4 | extern GLOBAL u64 mm_heapMax; 5 | 6 | static void mm_free(Value* x) { 7 | preFree(x, false); 8 | free(x); 9 | } 10 | 11 | static void* mm_alloc(u64 sz, u8 type) { 12 | Value* x = malloc(sz); 13 | preAlloc(sz, type); 14 | x->flags = x->extra = x->mmInfo = x->type = 0; 15 | x->refc = 1; 16 | x->type = type; 17 | return x; 18 | } 19 | 20 | #define gc_running 0 21 | static void gc_disable() { } 22 | static void gc_enable() { } 23 | static void mm_visit(B x) { } 24 | static void mm_visitP(void* x) { } 25 | 26 | bool gc_maybeGC(bool); 27 | void gc_forceGC(bool); 28 | #define gc_depth 0 29 | void mm_forHeap(V2v f); 30 | void mm_dumpHeap(FILE* f); 31 | 32 | static u64 mm_round(usz x) { return x; } 33 | static u64 mm_size(Value* x) { 34 | size_t r = malloc_usable_size(x); 35 | if (((ssize_t)r) < 16) fatal("MM=0 requires working malloc_usable_size"); 36 | return r; 37 | } 38 | static u64 mm_totalAllocated() { return -1; } 39 | -------------------------------------------------------------------------------- /src/utils/toplevel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | typedef ux Run; 3 | 4 | #ifndef TOPLEVEL_GC 5 | #define TOPLEVEL_GC 1 6 | #endif 7 | 8 | #if TOPLEVEL_GC 9 | void run_slow(); 10 | extern GLOBAL ux run_nesting; 11 | extern GLOBAL ux run_countdown; 12 | static void nneg_ux(ux x) { assert(x == (x<<1>>1)); } 13 | 14 | // must surround all entry points that may ever itself result in doing run_start+run_end 15 | // (allocating & inc/dec are okay (if at some point finalizers are added, they'll be deferred to GC or similar, and GC will ensure its own non-zero run_nesting); getters are currently okay) 16 | static Run run_start() { 17 | nneg_ux(run_nesting); 18 | return ++run_nesting; 19 | } 20 | static void run_end(Run e) { 21 | debug_assert(e == run_nesting); 22 | ux now = --run_nesting; 23 | nneg_ux(run_countdown-1); 24 | if (!now) { 25 | assert(run_countdown > 0); 26 | if (RARE(!--run_countdown)) run_slow(); 27 | } 28 | } 29 | #else 30 | static Run run_start() { return 0; } 31 | static void run_end(Run e) { } 32 | #endif -------------------------------------------------------------------------------- /test/cases/proper-fills.bqn: -------------------------------------------------------------------------------- 1 | %DEF fill Fill←•internal.HasFill◶'e'‿{⊑1↑0⥊𝕩} 2 | %DEF eqall EqAll ← {!∘≡⟜(⊑𝕩)¨ 𝕩 ⋄ ⊑𝕩} 3 | 4 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 4↑{⇐} 5 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 4↑{+} 6 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 4↑{¨} 7 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 4↑{∘} 8 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 4↑@‿1 9 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 2‿2↑@‿1 10 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 2‿2↑@‿1‿@ 11 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 3↑2‿2⥊@‿1 12 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % 3‿3↑2‿2⥊@‿1 13 | !"𝕨↑𝕩: Fill element of 𝕩 needed for overtaking" % ¯4↑@‿1 14 | 15 | !"»𝕩: Fill element of 𝕩 not known" % » 2‿2⥊{+} 16 | !"«𝕩: Fill element of 𝕩 not known" % «˘2‿2⥊0‿' ' 17 | 18 | !"𝕨⥊𝕩: Fill element of 𝕩 needed for overtaking" % 2‿↑ ⥊ @‿1‿@ 19 | 20 | !">𝕩: Fill element of empty 𝕩 not known" % >0⥊{⇐} 21 | !">𝕩: Fill element of empty 𝕩 not known" % >0‿0⥊{⇐} 22 | -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "h.h" 4 | #include "core/stuff.h" 5 | 6 | #if MM==0 7 | #include "opt/mm_malloc.h" 8 | #undef ENABLE_GC 9 | #define ENABLE_GC 0 10 | #elif MM==1 11 | #include "opt/mm_buddy.h" 12 | #elif MM==2 13 | #include "opt/mm_2buddy.h" 14 | #else 15 | #error "bad MM value" 16 | #endif 17 | 18 | #include "core/gstack.h" 19 | #include "core/harr.h" 20 | 21 | #include "core/numarr.h" 22 | #include "core/chrarr.h" 23 | #include "core/fillarr.h" 24 | 25 | #include "core/derv.h" 26 | #include "core/arrFns.h" 27 | 28 | #ifdef RT_VERIFY 29 | extern GLOBAL B r1Objs[RT_LEN]; 30 | #endif 31 | 32 | 33 | typedef struct BFn { 34 | struct Fun; 35 | u8 nid; 36 | B ident; 37 | FC2 uc1; 38 | BBBB2B ucw; 39 | FC1 im, is; 40 | FC2 iw; B rtInvSwap; 41 | FC2 ix; B rtInvReg; 42 | } BFn; 43 | typedef struct BMd1 { 44 | struct Md1; 45 | u8 nid; 46 | D1C1 im; 47 | D1C2 iw; 48 | D1C2 ix; 49 | } BMd1; 50 | 51 | typedef struct BMd2 { 52 | struct Md2; 53 | u8 nid; 54 | D2C1 im; 55 | D2C2 iw; 56 | D2C2 ix; 57 | M2C4 uc1; 58 | M2C5 ucw; 59 | } BMd2; 60 | -------------------------------------------------------------------------------- /test/cases/undo.bqn: -------------------------------------------------------------------------------- 1 | <⁼ <"foo" %% "foo" 2 | !"<⁼𝕩: Argument wasn't a rank 0 array" % <⁼ 4 3 | !"<⁼𝕩: Argument wasn't a rank 0 array" % <⁼ ↕0 4 | !"<⁼𝕩: Argument wasn't a rank 0 array" % <⁼ "f" 5 | 6 | ⋈⁼ ⋈"foo" %% "foo" 7 | ⋈⁼ "f" %% 'f' 8 | !"⋈⁼𝕩: Argument wasn't a length-1 list" % ⋈⁼ 4 9 | !"⋈⁼𝕩: Argument wasn't a length-1 list" % ⋈⁼ ↕0 10 | !"⋈⁼𝕩: Argument wasn't a length-1 list" % ⋈⁼ ≍"f" 11 | 12 | ≍⁼ ⋈"foo" %% <"foo" 13 | ≍⁼ ⋈5 %% <5 14 | ≍⁼ 1‿2‿3⥊↕6 %% 2‿3⥊↕6 15 | ≍⁼ 1‿2⥊↕2 %% ↕2 16 | !"≍⁼𝕩: Argument must have a leading axis of 1" % ≍⁼ 5 17 | !"≍⁼𝕩: Argument must have a leading axis of 1" % ≍⁼ <"foo" 18 | !"≍⁼𝕩: Argument must have a leading axis of 1" % ≍⁼ "foo" 19 | !"≍⁼𝕩: Argument must have a leading axis of 1" % ≍⁼ 3‿1‿1⥊<"foo" 20 | 21 | !"⌽𝕩: 𝕩 cannot be a unit" % ⌽⁼ 0 22 | ⌽⁼ ↕10 %% 9-↕10 23 | 24 | !"⍉⁼𝕩: 𝕩 must not be an atom" % ⍉⁼ 3 25 | !"𝕨⍉⁼𝕩: 𝕩 must not be an atom" % 0 ⍉⁼ 3 26 | !"𝕨⍉⁼𝕩: Length of 𝕨 must be at most rank of 𝕩" % 0‿0 ⍉⁼ ↕3 27 | !"𝕨⍉⁼𝕩: Axis 9 does not exist (1≡=𝕩)" % 9 ⍉⁼ ↕3 28 | !"𝕨⍉⁼𝕩: Axis 9 does not exist (3≡=𝕩)" % 9 ⍉⁼ 3‿3‿3⥊3 29 | !"𝕨⍉⁼𝕩: 𝕨 must have rank at most 1" % (1‿1⥊0) ⍉⁼ ↕3 30 | ⍉⁼ <3 %% <3 31 | -------------------------------------------------------------------------------- /src/windows/getline.c: -------------------------------------------------------------------------------- 1 | 2 | #include "getline.h" 3 | 4 | ssize_t getline (char **lptr, size_t *n, FILE *fp) { 5 | wchar_t buf[MAX_LINE_LENGTH/3] = {0}; 6 | DWORD bytes; 7 | DWORD chars, read_chars; 8 | 9 | int convertResult; 10 | char *m; 11 | 12 | bytes = MAX_LINE_LENGTH; 13 | chars = bytes/3; 14 | 15 | HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); 16 | 17 | if (!ReadConsoleW(hIn, &buf, chars, &read_chars, NULL)) { 18 | fprintf(stderr, "Failed to read console input: %d", (int)GetLastError()); 19 | goto error; 20 | } 21 | 22 | convertResult = WideCharToMultiByte(CP_UTF8, 0, buf, -1, NULL, 0, NULL, NULL); 23 | if (convertResult == 0) { 24 | fprintf(stderr, "Failed to get MultiByte length: %d", (int)GetLastError()); 25 | goto error; 26 | } 27 | 28 | m = *lptr = (char*) calloc(convertResult, sizeof(char)); 29 | 30 | if (WideCharToMultiByte(CP_UTF8, 0, buf, -1, m, convertResult, NULL, NULL) == 0 ) { 31 | fprintf(stderr, "Failed to convert wide characters: %d", (int)GetLastError()); 32 | free(m); 33 | goto error; 34 | } 35 | 36 | return convertResult-1; 37 | 38 | error: 39 | CloseHandle(hIn); 40 | return -1; 41 | } -------------------------------------------------------------------------------- /test/cases/build-specific/test_group_stat.bqn: -------------------------------------------------------------------------------- 1 | 298 •internal.Temp ↕10 %% 0‿0‿1‿10‿9 2 | 3 | %DEF tvar AllEq←{v←⊑𝕩 ⋄ {𝕩≡v?1;!𝕩‿v}¨ ⥊𝕩 ⋄ v} ⋄ _tvar ← {Sel _𝕣 a: AllEq {Sel 298 •internal.Temp 𝕩•internal.Variation a}¨ ∊⟜"Ai8"‿"Ai16"‿"Ai32"⊸/ •internal.ListVariations •internal.Squeeze a} 4 | %USE tvar ⋄ ⊢_tvar ↕10 %% 0‿0‿1‿10‿9 5 | 6 | %USE tvar ⋄ AllEq {a←𝕩⥊0 ⋄ AllEq {⊑_tvar ¯2⌾(𝕩⊸⊑) a}¨ ↕𝕩}¨ 1+↕40 %% 1 7 | %USE tvar ⋄ AllEq {a←𝕩⥊0 ⋄ AllEq {⊑_tvar ¯128⌾(𝕩⊸⊑) a}¨ ↕𝕩}¨ 1+↕40 %% 1 8 | 9 | %USE tvar ⋄ AllEq {⊢_tvar 𝕩⥊0}¨ 1+↕1000 %% 0‿0‿1‿1‿0 10 | %USE tvar ⋄ AllEq {⊢_tvar 𝕩⥊10}¨ 1+↕1000 %% 0‿0‿1‿1‿10 11 | %USE tvar ⋄ {! ⟨0,0,0,𝕩,3⟩ ≡ ⊢_tvar 𝕩⥊3‿2}¨ 2+↕1000 12 | %USE tvar ⋄ {! ⟨0,0,1,𝕩,𝕩-1⟩ ≡ ⊢_tvar ↕𝕩}¨ 1+↕1000 13 | %USE tvar ⋄ {! ⟨0,0,𝕩≤10,𝕩,9⌊𝕩-1⟩ ≡ ⊢_tvar 𝕩⥊↕10}¨ (1+↕10000) ∾ ≤⟜(2⋆20)⊸/ ⍷∧⌊⥊1.5‿2⋆⌜↕40 14 | %USE tvar ⋄ ⊢_tvar ↕0 %% 0‿0‿1‿0‿¯1 15 | 16 | %USE tvar ⋄ AllEq {a←𝕩⥊0 ⋄ AllEq {0‿1‿2‿4⊸⊏_tvar 3⌾(𝕩⊸⊑) a}¨ ↕𝕩-1}¨ 2+↕40 %% 0‿0‿0‿3 17 | %USE tvar ⋄ AllEq {a←𝕩⥊0 ⋄ AllEq { ⊢_tvar 9⌾(𝕩⊸⊑) a}¨ 1+↕𝕩-2}¨ 3+↕40 %% 0‿0‿0‿3‿9 18 | 19 | %USE tvar ⋄ AllEq {a←𝕩⥊0 ⋄ AllEq {0‿1‿4⊸⊏_tvar ¯1⌾(𝕩⊸⊑) a}¨ ↕𝕩}¨ 2+↕40 %% 0‿1‿0 20 | %USE tvar ⋄ AllEq {a←𝕩⥊0 ⋄ AllEq {0‿1‿4⊸⊏_tvar 12⌾(𝕩⊸⊑) a}¨ ↕𝕩}¨ 1+↕40 %% 0‿0‿12 21 | -------------------------------------------------------------------------------- /src/core/gstack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // #define GS_REALLOC // whether to dynamically realloc gStack 3 | #ifndef GS_SIZE 4 | #define GS_SIZE 65536 // if !GS_REALLOC, size in number of B objects of the global object stack 5 | #endif 6 | #ifndef ENV_SIZE 7 | #define ENV_SIZE 4096 // max recursion depth; GS_SIZE and C stack size may limit this 8 | #endif 9 | 10 | extern GLOBAL B* gStack; // points to after end 11 | extern GLOBAL B* gStackStart; 12 | extern GLOBAL B* gStackEnd; 13 | void print_gStack(void); 14 | 15 | static void gsReserve(u64 am) { 16 | #ifdef GS_REALLOC 17 | if (am>gStackEnd-gStack) { 18 | u64 n = gStackEnd-gStackStart + am + 500; 19 | u64 d = gStack-gStackStart; 20 | gStackStart = realloc(gStackStart, n*sizeof(B)); 21 | gStack = gStackStart+d; 22 | gStackEnd = gStackStart+n; 23 | } 24 | #elif DEBUG 25 | if ((i64)am > gStackEnd-gStack) thrM("Stack overflow"); 26 | #endif 27 | } 28 | 29 | void gsReserveR(u64 am); 30 | 31 | 32 | static inline void gsAdd(B x) { 33 | #ifdef GS_REALLOC 34 | if (gStack==gStackEnd) gsReserveR(1); 35 | #else 36 | if (gStack==gStackEnd) thrM("Stack overflow"); 37 | #endif 38 | *(gStack++) = x; 39 | } 40 | static inline B gsPop() { 41 | return *--gStack; 42 | } 43 | -------------------------------------------------------------------------------- /src/singeli/c/cmp.c: -------------------------------------------------------------------------------- 1 | #include "../../core.h" 2 | #include "../../builtins.h" 3 | #include 4 | 5 | extern bool please_tail_call_err; 6 | static NOINLINE void cmp_err() { if (please_tail_call_err) thrM("Invalid comparison"); } 7 | 8 | 9 | #define SINGELI_FILE cmp 10 | #include "../../utils/includeSingeli.h" 11 | 12 | FN_LUT_A(cmp_fns, eq, AS); FN_LUT_A(cmp_fns, eq, AA); 13 | FN_LUT_A(cmp_fns, ne, AS); FN_LUT_A(cmp_fns, ne, AA); 14 | FN_LUT_A(cmp_fns, gt, AS); FN_LUT_A(cmp_fns, gt, AA); 15 | FN_LUT_A(cmp_fns, ge, AS); FN_LUT_A(cmp_fns, ge, AA); 16 | FN_LUT_A(cmp_fns, lt, AS); 17 | FN_LUT_A(cmp_fns, le, AS); 18 | 19 | #define ARCH(N) simd_##N 20 | #define DSTE(N) &(cmp_fns_##N[0]) 21 | void cmpA_init(void) { 22 | { 23 | CmpAAFn* srcs[4] = {ARCH(eqAA), ARCH(neAA), ARCH(gtAA), ARCH(geAA)}; 24 | CmpAAFn* dsts[4] = {DSTE(eqAA), DSTE(neAA), DSTE(gtAA), DSTE(geAA)}; 25 | for (i32 i=0; i<4; i++) for (i32 j=0; j<8; j++) dsts[i][j] = srcs[i][j]; 26 | } 27 | { 28 | CmpASFn* srcs[6] = {ARCH(eqAS), ARCH(neAS), ARCH(gtAS), ARCH(geAS), ARCH(ltAS), ARCH(leAS)}; 29 | CmpASFn* dsts[6] = {DSTE(eqAS), DSTE(neAS), DSTE(gtAS), DSTE(geAS), DSTE(ltAS), DSTE(leAS)}; 30 | for (i32 i=0; i<6; i++) for (i32 j=0; j<8; j++) dsts[i][j] = srcs[i][j]; 31 | } 32 | } 33 | #undef ARCH 34 | #undef DSTE 35 | -------------------------------------------------------------------------------- /test/readTests.bqn: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env dbqn 2 | "Usage: ./test.bqn path/to/mlochbaum/BQN [-qsmevw] name | ./BQN"!2≤≠•args 3 | # q - no "finished" at end 4 | # s - only non-erroring 5 | # e - only erroring 6 | # v - verify that the result is the expected one 7 | # w - wrap each expression in {}s so it can be a single compilation unit 8 | path←•wdpath•file.At ⊑•args 9 | args←1↓•args 10 | opt←"" 11 | {𝕊: opt↩𝕩 ⋄ args↓˜↩1}⍟('-'≡⊑) ⊑args 12 | verify ← ⊑'v'∊opt 13 | wrap ← ⊑'w'∊opt 14 | onlyErr ← ⊑'e'∊opt 15 | onlyReg ← ⊑'s'∊opt 16 | 17 | "Missing argument!"!0<≠args 18 | { 19 | case ← 𝕩∾".bqn" 20 | tests ← •FLines path∾"/test/cases/"∾case 21 | tests/˜↩ (("#"≢1⊸↑)∧0<≠)¨tests 22 | {𝕊: tests/˜↩'!'≠⊑¨tests}⍟⊢ onlyReg 23 | {𝕊: tests/˜↩'!'=⊑¨tests}⍟⊢ onlyErr 24 | RMC ← ⊐⟜'#'⊑⊸↑⊢ # doesn't work for token.bqn but whatever 25 | Q ← { 26 | i←⊑𝕩⊐'%' 27 | ((1+'!'≠⊑𝕩)×verify)◶⟨(1+i)⊸↓ ⋄ {∾ ⟨"""should've errored!""∘•BQN⎊1 """ ⋄ {𝕩/˜1+𝕩='"'}𝕩↓˜1+⊑𝕩⊐'%' ⋄ """"⟩} ⋄ {∾⟨"""incorrect!""‿1⊑˜(" ⋄ i↑𝕩 ⋄ ") ≡ {" ⋄ (1+i)↓𝕩 ⋄ " }"⟩}⟩ RMC 𝕩 28 | } 29 | Out ← {•Out(⊑'%'⊸∊)◶⟨{∾⟨"""incorrect!""‿1⊑˜1≡{"⋄RMC𝕩⋄" }"⟩}⍟verify {∾⟨"{"⋄RMC𝕩⋄"}"⟩}⍟wrap ⋄ Q⟩ 𝕩} 30 | 31 | ((⊑'m'∊opt)⊑Out‿{•Show𝕩⋄Out𝕩})¨tests 32 | # ('%'⊸∊∨ '#'= ·⊑ ∾⟜"#")◶•Out‿@¨tests 33 | }¨args 34 | •Out⍟(¬⊑'q'∊opt) """finished""" 35 | •Out⍟(¬⊑'q'∊opt) "" -------------------------------------------------------------------------------- /src/opt/mm_2buddy.h: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | typedef struct EmptyValue EmptyValue; 4 | struct EmptyValue { // needs set: mmInfo; type=t_empty; next; everything else can be garbage 5 | struct Value; 6 | EmptyValue* next; 7 | }; 8 | #if OBJ_COUNTER 9 | extern GLOBAL u64 currObjCounter; 10 | #endif 11 | extern GLOBAL u64 mm_heapAlloc; 12 | extern GLOBAL u64 mm_heapMax; 13 | 14 | extern GLOBAL u64 mm_ctrs[128]; 15 | extern GLOBAL EmptyValue* mm_buckets[128]; 16 | #define BSZ(X) (((X)&64? 3ull : 1ull)<<(X)) 17 | #define BN(X) mm_##X 18 | #include "mm_buddyTemplate.h" 19 | 20 | 21 | #define LOG2(X) ((u8)(64-CLZ((X)-1ull))) 22 | 23 | #if !ALLOC_NOINLINE || ALLOC_IMPL || ALLOC_IMPL_MMX 24 | static void* mm_alloc(u64 sz, u8 type) { 25 | assert(sz>=16); 26 | u32 log = LOG2(sz); 27 | u32 logm2 = log-2; 28 | bool b2 = sz <= (3ull<mmInfo; 41 | if (m&64) return 3ull<<(x->mmInfo&63); 42 | else return 1ull<<(x->mmInfo&63); 43 | } 44 | void mm_forHeap(V2v f); 45 | void mm_dumpHeap(FILE* f); 46 | 47 | #undef BN 48 | #undef BSZ 49 | #undef LOG2 -------------------------------------------------------------------------------- /test/cases/fuzz/squeezeValid.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # tests if squeeze produces valid results 3 | helpers ← ⊑•args 4 | r ← helpers.GetRand@ 5 | ⟨EEqual, ListVariations, ClearRefs, Variation, Unshare, Squeeze, Info⟩ ← •internal 6 | ⟨RandVals, vars, spec, specF64⟩ ← ⟨r⟩ helpers.Import "utils.bqn" 7 | 8 | TestSq0 ← {a ← Squeeze 𝕨 Variation 𝕩 ⋄ ! 1==a ⋄ ! 0=≠a ⋄ a} 9 | {! ⟨0⟩≡1↑𝕩 TestSq0 ↕0}¨ 5↑vars 10 | {! " "≡1↑𝕩 TestSq0 ""}¨ 5↓vars 11 | {𝕩 TestSq0 ⟨⟩}¨ ListVariations ⟨⟩ # {𝕩{•Out "Failed fill for "∾𝕨}⍟⊢ 8 ≠ 1↑⎊8 𝕩 TestSq0 ⟨⟩}¨ ListVariations ⟨⟩ # %ALLOW_CATCH 12 | 13 | specSpl ← 5(↑⋈↓)spec 14 | allSpec ← specF64 ∾ (@+0‿10‿32‿127‿128‿255‿256‿32767‿32768‿65535‿65536‿1114110‿1114111) ∾ {⇐}‿+ 15 | Do ← { 𝕊: 16 | at ← r.Range 8 17 | l ← 1 + r.Range (at=0)⊑1000‿300 18 | a ← l RandVals at 19 | {! a EEqual Squeeze 𝕩 Variation a}¨ ListVariations a 20 | 21 | F ← { 𝕊: 22 | p ← {r.Range 2? r.Range l; (¯1+l-⊢)⍟(r.Range 2) r.Range (2⋆r.Range 10)⌊l} 23 | b ← 𝕩˙⌾(p⊸⊑) a 24 | b0 ← Unshare b 25 | ¬b0 EEqual Squeeze b? 26 | •Out "Fail:" 27 | •Out "Array: "∾•Repr b 28 | •Out "Squeezed: "∾•Repr Squeeze b 29 | !0 30 | ;@ 31 | } 32 | 33 | F¨ (at≥5)⊑specSpl 34 | F¨ allSpec 35 | {𝕊: F¨ @⊸+⍟(at≥5) r.Range 2⋆r.Range (at≥5)⊑33‿20}¨ ↕10 36 | ClearRefs@ 37 | } 38 | 39 | Do⍟40000 @ 40 | -------------------------------------------------------------------------------- /src/core/tyarrTemplate.h: -------------------------------------------------------------------------------- 1 | #define T_ARR TP(t_,arr) 2 | #define T_SLICE TP(t_,slice) 3 | #define TEl JOIN(TU,Atom) 4 | typedef TyArr JOIN(TU,Arr); 5 | typedef TP(,) JOIN(TU,Atom); 6 | 7 | SHOULD_INLINE B TP(m_,arrv) (TEl** p, u64 ia) { 8 | CHECK_IA(ia, sizeof(TEl)); 9 | TyArr* r = m_arr(TYARR_SZ2(TU,ia), T_ARR, ia); 10 | arr_shVec((Arr*)r); 11 | *p = (TEl*)r->a; 12 | return taga(r); 13 | } 14 | SHOULD_INLINE B TP(m_,arrc) (TEl** p, B x) { assert(isArr(x)); 15 | usz ia = IA(x); 16 | CHECK_IA(ia, sizeof(TEl)); 17 | TyArr* r = m_arr(TYARR_SZ2(TU,ia), T_ARR, IA(x)); 18 | *p = (TEl*)r->a; 19 | arr_shCopy((Arr*)r, x); 20 | return taga(r); 21 | } 22 | SHOULD_INLINE Arr* TP(m_,arrp) (TEl** p, u64 ia) { 23 | CHECK_IA(ia, sizeof(TEl)); 24 | TyArr* r = m_arr(TYARR_SZ2(TU,ia), T_ARR, ia); 25 | *p = (TEl*)r->a; 26 | return (Arr*)r; 27 | } 28 | 29 | static TEl* TP(,anyv_ptr) (Arr* x) { 30 | u8 t = PTY(x); 31 | if (t == T_ARR) return (TEl*) ((TyArr*) x)->a; 32 | assert(t == T_SLICE); 33 | return (TEl*) ((TySlice*) x)->a; 34 | } 35 | static TEl* TP(,arrv_ptr) (TyArr* x) { return (TEl*) ((TyArr*) x)->a; } 36 | static TEl* TP(,arr_ptr) (B x) { VTY(x, T_ARR); return TP(,arrv_ptr)((TyArr*) a(x)); } 37 | static TEl* TP(,any_ptr) (B x) { assert(isArr(x)); return TP(,anyv_ptr)(a(x)); } 38 | 39 | #undef TEl 40 | #undef TU 41 | #undef TP 42 | -------------------------------------------------------------------------------- /test/cases/unnecessary/fuzz-slash.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # generally unnecessary now that debug builds include heap corruption testing 3 | ⟨LV⇐ListVariations, V⇐Variation, ClearRefs⇐ClearRefs, I⇐Info, TY⇐Type, Refc⇐Refc, EEQ⇐EEqual⟩ ← •internal 4 | helpers ← ⊑•args 5 | r0 ← helpers.GetRand@ 6 | R ← r0.Range 7 | RB2 ← {¬⍟(R 2) "Ab" V 𝕩 R 1+R 2⋆R 10} # random boolean array with random uniform probability 8 | 9 | rByteVals ← "Ai8"V {𝕩-256×𝕩>127} ∾{{2⊸×⊸+˜´ 8↑/⁼ 𝕩 r0.Subset 8}¨ ↕9}¨↕20 10 | RByte ← (R (≠rByteVals)˙)⊸(⊑⟜rByteVals) 11 | 12 | RH ← "Ai8"⊸V¨ R⥊¨RByte # store to a variable to randomize heap by 𝕨 values, each up to 𝕩 bytes 13 | CH ← { # do "var CH↩" to a variable assigned to a result of RH 14 | "heap corruption" ! ∧´2=Refc¨ 𝕩 15 | "heap corruption" ! 1 ≡ Refc 𝕩 16 | 0 17 | } 18 | BitSlash ← {ty‿dy‿max𝕊n: 19 | f ← dy ⊑ ⟨/∘⊢ ⋄ {𝕩/ty V 𝕨 R 100}⟩ 20 | {𝕊: 21 | # 𝕩⊸{•Show 𝕨}⍟⊢ 0=10000|𝕩 22 | t1←20 RH 100 23 | n←R max 24 | x←n F n↑RB2 128+n 25 | t2←20 RH 100 26 | x↩0 ⋄ t1 CH↩ ⋄ t2 CH↩ 27 | }¨↕n 28 | } 29 | •Show "heap corruption test of /bit" ⋄ @‿0‿200 BitSlash 1000000 30 | •Show "heap corruption test of bit/i8" ⋄ "Ai8"‿1‿50 BitSlash 1000000 31 | •Show "heap corruption test of bit/i16" ⋄ "Ai16"‿1‿50 BitSlash 1000000 32 | •Show "heap corruption test of bit/i32" ⋄ "Ai32"‿1‿50 BitSlash 1000000 33 | -------------------------------------------------------------------------------- /test/cases/fuzz/copy.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # tests full array copying via •internal.Variation 3 | helpers ← ⊑•args 4 | R ← (helpers.GetRand@).Range 5 | ⟨Variation, ListVariations, ClearRefs⟩ ⇐ •internal 6 | 7 | vn ← "Ab"‿"Ai8"‿"Ai16"‿"Ai32"‿"Af64"‿"Ac8"‿"Ac16"‿"Ac32"‿"Ah"‿"Af" 8 | vi ←⟨0, 0, 0, 0, 1, 2, 2, 2, 1, 1⟩ 9 | 10 | vf ← ∾⟨ 11 | ↓⟜(5↑ vn)¨ ↕5 12 | ↓⟜(3↑5↓vn)¨ ↕3 13 | ⟨⟩‿⟨⟩ 14 | ⟩ 15 | vf ↩ ∾⟜(8↓vn)¨ vf 16 | vf ↩ (↕10) {𝕩 ∾ 'S'∾¨ 1↓¨ (𝕨=0)↓𝕩}¨ vf 17 | vf ↩ {𝕩 ∾ ∾⟜"Inc"¨ 𝕩}¨ vf 18 | 19 | # •Show¨ vf 20 | 21 | count ← 10000 22 | eqlen ← 0 23 | 24 | {𝕊: 25 | n ← R 2⋆5+R 9 26 | shl ← 1⊸+•_while_{(R 2)∧0=1|n÷2⋆𝕩+1} 0 27 | sh ← (n÷2⋆shl)∾shl⥊2 28 | {𝕊: sh↩⟨⟩}⍟⊢ (n=1) ∧ 0=R 2 29 | 30 | ! n ≡ ×´sh 31 | v ← R ≠vn 32 | l ← sh ⥊ ⊢◶⟨ 33 | {𝕊: (v⊑0∾2⋆6‿14‿30) -˜ n R 2⋆v⊑1‿7‿15‿31} 34 | {𝕊: ÷n R 0} 35 | {𝕊: @+n R (v-5)⊑256‿65536‿1114112} 36 | ⟩ v⊑vi 37 | 38 | vs ← v⊑vf 39 | vsLV ← ListVariations l 40 | 41 | { 𝕊 v0: 42 | a0 ← v0 Variation l 43 | ! a0 ≡ l 44 | { 𝕊: 45 | a1 ← 𝕩 Variation a0 46 | a0 ≡ a1?1; 47 | •Out "fail:" 48 | •Show v0‿"→"‿𝕩 49 | !0 50 | }¨ vs 51 | }¨ vs 52 | ClearRefs@ 53 | eqlen+↩ (≠vs) ≡ +´⥊vs≡⌜vsLV 54 | }¨ ↕count 55 | 56 | •Out "Fraction of matching variation count to ListVariations: "∾(•Repr eqlen÷count)∾" (expected: ~0.999)" -------------------------------------------------------------------------------- /src/core/chrarr.h: -------------------------------------------------------------------------------- 1 | #define c8 u8 2 | #define c16 u16 3 | #define c32 u32 4 | 5 | #define TU C8 6 | #define TP(W,X) W##c8##X 7 | #include "tyarrTemplate.h" 8 | #define TU C16 9 | #define TP(W,X) W##c16##X 10 | #include "tyarrTemplate.h" 11 | #define TU C32 12 | #define TP(W,X) W##c32##X 13 | #include "tyarrTemplate.h" 14 | 15 | #undef c8 // still not sure if i want these 16 | #undef c16 17 | #undef c32 18 | 19 | 20 | B m_c32vec_0(u32* s); 21 | B m_c32vec(u32* s, i64 sz); 22 | B m_c8vec_0(char* s); 23 | B m_c8vec(char* s, i64 sz); 24 | 25 | B utf8Decode0(const char* x); 26 | B utf8Decode(const char* x, i64 sz); 27 | B utf8DecodeA(I8Arr* x); 28 | 29 | Arr* cpyC8Arr (B x); // consumes 30 | Arr* cpyC16Arr(B x); // consumes 31 | Arr* cpyC32Arr(B x); // consumes 32 | 33 | // all consume x 34 | static C8Arr* toC8Arr (B x) { return TY(x)==t_c8arr ? c(C8Arr, x) : (C8Arr*) cpyC8Arr (x); } 35 | static C16Arr* toC16Arr(B x) { return TY(x)==t_c16arr? c(C16Arr,x) : (C16Arr*) cpyC16Arr(x); } 36 | static C32Arr* toC32Arr(B x) { return TY(x)==t_c32arr? c(C32Arr,x) : (C32Arr*) cpyC32Arr(x); } 37 | 38 | static B toC8Any (B x) { u8 t=TY(x); return t==t_c8arr || t==t_c8slice ? x : taga(cpyC8Arr (x)); } 39 | static B toC16Any(B x) { u8 t=TY(x); return t==t_c16arr || t==t_c16slice? x : taga(cpyC16Arr(x)); } 40 | static B toC32Any(B x) { u8 t=TY(x); return t==t_c32arr || t==t_c32slice? x : taga(cpyC32Arr(x)); } 41 | -------------------------------------------------------------------------------- /src/singeli/src/avx.singeli: -------------------------------------------------------------------------------- 1 | # f32 arith 2 | def rsqrtE{a:T==[8]f32} = emit{T, '_mm256_rsqrt_ps', a} 3 | def rcpE{a:T==[8]f32} = emit{T, '_mm256_rcp_ps', a} 4 | 5 | # conversion 6 | def pair{a:T,b:T if w128{T}} = n_d{T} ~~ emit{[8]i32, '_mm256_setr_m128i', a, b} 7 | 8 | def widen{T==[4]f64, x:([4]i32)} = emit{T, '_mm256_cvtepi32_pd', x} 9 | def widen{T==[4]f64, x:([4]f32)} = emit{T, '_mm256_cvtps_pd', x} 10 | def widen{T==[4]f64, x:X=[_]U if w128i{X} and width{U}<32} = widen{T, widen{[4]i32, x}} 11 | def widen{T=[k]_, x:X=[l]_ if w256{X} and l>k} = widen{T, half{x,0}} 12 | 13 | 14 | # structural operations 15 | def blend_top{f:T, t:T, m:M if w256i{T,32} and w256i{M,32}} = T ~~ blend_top{v2f{f}, v2f{t}, v2f{m}} 16 | def blend_top{f:T, t:T, m:M if w256i{T,64} and w256i{M,64}} = T ~~ blend_top{v2d{f}, v2d{t}, v2d{m}} 17 | 18 | # mask stuff 19 | def top_to_int{x:T if w256{T, 32}} = emit{u8, '_mm256_movemask_ps', v2f{x}} 20 | def top_to_int{x:T if w256{T, 64}} = emit{u8, '_mm256_movemask_pd', v2d{x}} 21 | def hom_to_int{x:T if w256{T}} = top_to_int{x} 22 | 23 | def any_hom{x:T if w256i{T} and elwidth{T}>=32} = hom_to_int{[8]u32 ~~ x} != 0 24 | def all_hom{x:T if w256i{T} and elwidth{T}>=32} = hom_to_int{[8]u32 ~~ x} == 0xff 25 | 26 | def any_top{x:T=[_]E if w256i{T} and width{E}>=32} = top_to_int{x} != 0 27 | def all_top{x:T=[k]E if w256i{T} and width{E}>=32} = top_to_int{x} == tail{k} 28 | -------------------------------------------------------------------------------- /src/opt/mm_buddyTemplate.h: -------------------------------------------------------------------------------- 1 | #define buckets BN(buckets) 2 | 3 | #if !ALLOC_NOINLINE || ALLOC_IMPL || ALLOC_IMPL_MMX 4 | 5 | FORCE_INLINE void BN(freeLink)(Value* x, bool link) { 6 | #if ALLOC_IMPL_MMX 7 | preFree(x, true); 8 | #else 9 | preFree(x, false); 10 | #endif 11 | #if DONT_FREE 12 | if (x->type!=t_freed) x->flags = x->type; 13 | #else 14 | u8 b = x->mmInfo&127; 15 | BN(ctrs)[b]--; 16 | if (link) { 17 | ((EmptyValue*)x)->next = buckets[b]; 18 | buckets[b] = (EmptyValue*)x; 19 | } 20 | #endif 21 | x->type = t_empty; 22 | vg_undef_p(x, BSZ(x->mmInfo&127)); 23 | } 24 | 25 | ALLOC_FN void BN(free)(Value* x) { 26 | BN(freeLink)(x, true); 27 | } 28 | 29 | NOINLINE void* BN(allocS)(ux bucket, u8 type); 30 | static void* BN(allocL)(ux bucket, u8 type) { 31 | EmptyValue* x = buckets[bucket]; 32 | if (RARE(x==NULL)) return BN(allocS)(bucket, type); 33 | buckets[bucket] = vg_def_v(x->next); 34 | BN(ctrs)[bucket]++; 35 | x->flags = x->extra = x->type = x->mmInfo = 0; 36 | x->refc = 1; 37 | x->type = type; 38 | x->mmInfo = bucket; 39 | #if OBJ_COUNTER 40 | x->uid = currObjCounter++; 41 | #ifdef OBJ_TRACK 42 | if (x->uid == OBJ_TRACK) { 43 | printf("Tracked object "N64u" created at:\n", (u64)OBJ_TRACK); 44 | vm_pstLive(); 45 | } 46 | #endif 47 | #endif 48 | return x; 49 | } 50 | #endif 51 | #undef buckets 52 | -------------------------------------------------------------------------------- /licenses/LICENSE-Boost: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /test/cases/fuzz/is-sorted.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | helpers ← ⊑•args 3 | r ← helpers.GetRand@ 4 | ⟨ListVariations, ClearRefs, Variation, ObjFlags⟩ ← •internal 5 | CF ← 0⊸ObjFlags 6 | 7 | Rn ← r.Range 8 | nan ← 0÷0 9 | 10 | {𝕊: 11 | Rl ← {𝕊: 0⌈¯2+⌊2⋆1+(¯1+2⋆⁼𝕩) × ×˜𝕨 Rn 0} 12 | ok ← Rn 2 13 | bad ← ¬ok 14 | m ← 500 15 | bm ← m×8 16 | 17 | arr ← {(Rn≠𝕩)◶𝕩 @} ⟨ 18 | {𝕊: # bitarr 19 | ok? ⟨1+Rl bm, 1+Rl bm⟩ / 0‿1 20 | ; ⟨Rl bm, 1+Rl bm, 1+Rl bm, Rl bm⟩ / 0‿1‿0‿1 21 | } 22 | {𝕊: # integer & float 23 | n ← 2+Rl m 24 | float ← Rn 2 25 | fnan ← float ∧ 1=Rn 3 26 | 27 | ds ← {float? n Rn 0; n Rn 2+Rn 10} 28 | 29 | ds (-1+Rl 1000)⌾((1+Rn n-1)⊸⊑)⍟(bad∧¬fnan)↩ 30 | mul ← 1+Rl 1e9 31 | r ← mul × (+`ds) - Rl m 32 | r nan⌾((Rn ¯1+≠r)⊸⊑)⍟(bad∧fnan)↩ 33 | r {𝕩∾nan⥊˜Rl 20}⍟float↩ 34 | } 35 | {𝕊: # char 36 | n ← 2+Rl m 37 | ds ← n Rn 2+Rn 10 38 | e ← 1114111 39 | r ← e⌊ (Rl e÷≠n)×+`ds 40 | r {a‿·: a {𝕊: e⌊1+Rl e×1.1}⍟(0=⊢)↩ ⋄ ⟨a, a-1+Rl a⟩}⌾((0‿1+Rn n-1)⊸⊏)⍟bad↩ 41 | r {𝕩 + 0⌈(⌈´𝕩)-˜ Rn∘≠⊸⊑ 0‿0‿255‿65535‿e}↩ 42 | @+r 43 | } 44 | ⟩ 45 | arr •internal.Squeeze↩ 46 | 47 | ex ← ⊑arr 48 | _test ← {{𝕊: c←2=•Type ex ⋄ ⟨ok,c,-⟜@⍟c arr⟩}⍟¬⊸! ok{𝕗?¬∨´;∧´} (𝔽 Variation⟜𝕩)¨ ListVariations 𝕩} 49 | {𝕩 0∘⍋⎊1 ex} _test CF arr 50 | {𝕩 0∘⍒⎊1 ex} _test CF ⌽arr 51 | ClearRefs@ 52 | @ 53 | }¨ ↕5000 54 | -------------------------------------------------------------------------------- /test/cases/imports.bqn: -------------------------------------------------------------------------------- 1 | %DEF file ( 2 | ⟨AtRoot, Import⟩ ← ⊑•args 3 | File ← AtRoot 4 | CleanPath ← ! {r←•file.RealPath AtRoot"" ⋄ ≠i←/r ⍷ 𝕩? (⋈⁼i)(↑∾"..."∾(≠r)⊸+⊸↓)𝕩; 𝕩}∘•CurrentError∘@ 5 | ) 6 | 7 | ( 8 | %USE file 9 | f ← File "imports/show.bqn" 10 | r1 ← •Import f ⋄ ! +‿"show.bqn"‿⟨⟩ ≡ 1↓r1 11 | r2 ← •Import f ⋄ ! +‿"show.bqn"‿⟨⟩ ≡ 1↓r2 12 | ! r1 ≡○⊑ r2 13 | re ← •ReBQN{primitives⇐⟨'+'‿-⟩} 14 | import2 ← RE "•Import" 15 | r3 ← Import2 f ⋄ ! -‿"show.bqn"‿⟨⟩ ≡ 1↓r3 16 | r4 ← Import2 f ⋄ ! -‿"show.bqn"‿⟨⟩ ≡ 1↓r4 17 | ! (⊑¨r1‿r3) ≡ ⍷⊑¨r1‿r2‿r3‿r4 18 | ) 19 | 20 | !"•Import 𝕩: cyclic import of "".../imports/cyclic.bqn""" % %USE file ⋄ Import⎊CleanPath "imports/cyclic.bqn" 21 | !"•Import 𝕩: cyclic import of "".../imports/cyclic.bqn""" % %USE file ⋄ Import⎊CleanPath "imports/cyclic.bqn" 22 | !"•Import 𝕩: cyclic import of "".../imports/cyclic.bqn""" % %USE file ⋄ ⟨⟩Import⎊CleanPath "imports/cyclic.bqn" 23 | !"•Import 𝕩: cyclic import of "".../imports/cyclic_A.bqn""" % %USE file ⋄ Import⎊CleanPath "imports/cyclic_A.bqn" 24 | !"•Import 𝕩: cyclic import of "".../imports/cyclic_B.bqn""" % %USE file ⋄ Import⎊CleanPath "imports/cyclic_B.bqn" 25 | !"•Import 𝕩: cyclic import of "".../imports/cyclic_A.bqn""" % %USE file ⋄ ⟨⟩Import⎊CleanPath "imports/cyclic_B.bqn" 26 | !"•Import 𝕩: cyclic import of "".../imports/cyclic_B.bqn""" % %USE file ⋄ ⟨⟩Import⎊CleanPath "imports/cyclic_A.bqn" 27 | %USE file ⋄ 3 Import "imports/notCyclic.bqn" %% 30 28 | -------------------------------------------------------------------------------- /src/opt/mm_buddy.h: -------------------------------------------------------------------------------- 1 | #include "gc.h" 2 | 3 | typedef struct EmptyValue EmptyValue; 4 | struct EmptyValue { // needs set: mmInfo; type=t_empty; next; everything else can be garbage 5 | struct Value; 6 | EmptyValue* next; 7 | }; 8 | #if OBJ_COUNTER 9 | extern GLOBAL u64 currObjCounter; 10 | #endif 11 | extern GLOBAL u64 mm_heapAlloc; 12 | extern GLOBAL u64 mm_heapMax; 13 | 14 | extern GLOBAL u64 mm_ctrs[64]; 15 | extern GLOBAL EmptyValue* mm_buckets[64]; 16 | #define BSZ(X) (1ull<<(X)) 17 | #define BN(X) mm_##X 18 | #include "mm_buddyTemplate.h" 19 | 20 | 21 | #define LOG2(X) ((u8)(64-CLZ((X)-1ull))) 22 | 23 | #if !ALLOC_NOINLINE || ALLOC_IMPL || ALLOC_IMPL_MMX 24 | ALLOC_FN void* mm_alloc(u64 sz, u8 type) { 25 | assert(sz>=16); 26 | NOGC_CHECK("allocating during noalloc"); 27 | preAlloc(sz, type); 28 | #if VERIFY_TAIL 29 | ux logAlloc = LOG2(sz + VERIFY_TAIL); 30 | #else 31 | ux logAlloc = LOG2(sz); 32 | #endif 33 | void* res = mm_allocL(logAlloc, type); 34 | #if VERIFY_TAIL && !ALLOC_IMPL_MMX 35 | tailVerifyAlloc(res, sz, logAlloc, type); 36 | #endif 37 | return res; 38 | } 39 | #endif 40 | 41 | static u64 mm_round(usz sz) { 42 | return BSZ(LOG2(sz)); 43 | } 44 | static u64 mm_size(Value* x) { 45 | return BSZ(x->mmInfo&63); 46 | } 47 | #if VERIFY_TAIL 48 | static u64 mm_sizeUsable(Value* x) { 49 | return mm_size(x) - VERIFY_TAIL; 50 | } 51 | #endif 52 | void mm_forHeap(V2v f); 53 | void mm_dumpHeap(FILE* f); 54 | 55 | #undef LOG2 56 | #undef BN 57 | #undef BSZ 58 | -------------------------------------------------------------------------------- /src/singeli/src/vecfold.singeli: -------------------------------------------------------------------------------- 1 | if_inline (hasarch{'X86_64'}) { 2 | # Fold associative/commutative operation across a register 3 | def vfold{F, x:V=[_]T if w128{V}} = { 4 | c:= x 5 | def EW = width{T} 6 | if (EW<=64) c = F{c, shuf{u64, c, 1,0}} 7 | if (EW<=32) c = F{c, shuf{u32, c, 1,0}} 8 | if (EW<=16) c = F{c, vec_shuffle16_lo{c, tup{1,0,3,2}}} 9 | if (EW==8) { v:=extract{[8]i16~~c, 0}; F{cast_i{T, v}, cast_i{T, v>>8}} } 10 | else extract{c, 0} 11 | } 12 | def vfold{(__add), x:V=[16]E if width{E}==8} = { 13 | c:= x + shuf{u64, x, 1,0} 14 | cast_i{E, extract{absdiff_sum{8, ty_u{c}, [16]u8**0}, 0}} 15 | } 16 | 17 | def vfold{F, x:T if w256{T}} = vfold{F, F{half{x, 0}, half{x, 1}}} 18 | 19 | 20 | 21 | # def fold_addw{DE, x:V=[k](i8) if DE>i8 and DE<=i64} = cast_i{DE, vfold{+, mul_sum_sat{2, x, [k]u8**1}}} 22 | def fold_addw{DE, x:V=[k](u8) if DE> u8} = cast_i{DE, vfold{+, absdiff_sum{8, x, V**0}}} 23 | def fold_addw{DE, x:V=[k](i16) if DE>i16} = cast_i{DE, vfold{+, mul_sum{2, x, V**1}}} 24 | def fold_addw{DE, x:V=[k](i8) if DE> i8} = DE ~~ fold_addw{ty_u{DE}, ty_u{x} ^ [k]u8 ** (1<< 7)} - k*(1<<7) 25 | def fold_addw{DE, x:V=[k](u16) if DE>u16} = DE ~~ fold_addw{ty_s{DE}, ty_s{x} ^ [k]i16** -(1<<15)} + k*(1<<15) 26 | def fold_addw{DE, x:V=[k](i32) if DE>i32} = DE ~~ fold_addw{ty_u{DE}, ty_u{x} ^ [k]u32** (1<<31)} - k*(1<<31) 27 | def fold_addw{DE, x:V=[k](u32) if DE>u32} = { vfold{+, el_m{blend_units{V**0,x,1,0}} + el_m{x}>>32} } 28 | } 29 | -------------------------------------------------------------------------------- /src/core/derv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Md1D { // F _md 4 | struct Fun; 5 | Md1* m1; 6 | B f; 7 | }; 8 | struct Md2D { // F _md_ G 9 | struct Fun; 10 | Md2* m2; 11 | B f, g; 12 | }; 13 | typedef struct Fork { 14 | struct Fun; 15 | B f, g, h; 16 | } Fork; 17 | typedef struct Atop { 18 | struct Fun; 19 | B g, h; 20 | } Atop; 21 | 22 | B md1D_c1(B t, B x); 23 | B md1D_c2(B t, B w, B x); 24 | B md2D_c1(B t, B x); 25 | B md2D_c2(B t, B w, B x); 26 | B tr2D_c1(B t, B x); 27 | B tr2D_c2(B t, B w, B x); 28 | B fork_c1(B t, B x); 29 | B fork_c2(B t, B w, B x); 30 | // consume all args 31 | static B m_md1D(Md1* m, B f ) { Md1D* r = mm_alloc(sizeof(Md1D), t_md1D); r->f = f; r->m1 = m; r->c1=md1D_c1; r->c2=md1D_c2; return tag(r,FUN_TAG); } 32 | static B m_md2D(Md2* m, B f, B g) { Md2D* r = mm_alloc(sizeof(Md2D), t_md2D); r->f = f; r->m2 = m; r->g = g; r->c1=md2D_c1; r->c2=md2D_c2; return tag(r,FUN_TAG); } 33 | static B m_fork(B f, B g, B h) { Fork* r = mm_alloc(sizeof(Fork), t_fork); r->f = f; r->g = g; r->h = h; r->c1=fork_c1; r->c2=fork_c2; return tag(r,FUN_TAG); } 34 | static B m_atop( B g, B h) { Atop* r = mm_alloc(sizeof(Atop), t_atop); r->g = g; r->h = h; r->c1=tr2D_c1; r->c2=tr2D_c2; return tag(r,FUN_TAG); } 35 | 36 | // consume all args 37 | static B m1_d(B m, B f ) { if(isMd1(m)) return TI(m,m1_d)(m, f ); thrM("Interpreting non-1-modifier as 1-modifier"); } 38 | static B m2_d(B m, B f, B g) { if(isMd2(m)) return TI(m,m2_d)(m, f, g); thrM("Interpreting non-2-modifier as 2-modifier"); } 39 | -------------------------------------------------------------------------------- /src/opt/mm_2buddy.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #if !NO_MMAP 3 | #include 4 | #endif 5 | #include "gc.c" 6 | 7 | #if OBJ_COUNTER 8 | GLOBAL u64 currObjCounter; 9 | #endif 10 | #if VERIFY_TAIL 11 | #error MM=2 doesn't support VERIFY_TAIL 12 | #endif 13 | 14 | #define ALLOC_MODE 0 15 | GLOBAL u64 mm_ctrs[128]; 16 | GLOBAL EmptyValue* mm_buckets[128]; 17 | #define b1_buckets mm_buckets 18 | #define b1_allocL mm_allocL 19 | #define b1_ctrs mm_ctrs 20 | #define ALSZ 20 21 | #define BSZ(X) (1ull<<(X)) 22 | #define MUL 1 23 | #define MMI(X) X 24 | #define BN(X) b1_##X 25 | #include "mm_buddyTemplate.c" 26 | #undef BN 27 | #undef BSZ 28 | 29 | #define b3_buckets (mm_buckets+64) 30 | #define b3_allocL mm_allocL 31 | #define b3_ctrs (mm_ctrs+64) 32 | #define ALSZ 20 33 | #define BSZ(X) (3ull<<(X)) 34 | #define MUL 3 35 | #define MMI(X) ((X)|64) 36 | #define BN(X) b3_##X 37 | #include "mm_buddyTemplate.c" 38 | #undef BN 39 | #undef BSZ 40 | #undef ALLOC_MODE 41 | 42 | NOINLINE void* mm_allocS(ux bucket, u8 type) { 43 | return bucket&64? b3_allocS(bucket, type) : b1_allocS(bucket, type); 44 | } 45 | 46 | void mm_forHeap(V2v f) { 47 | b1_forHeap(f); 48 | b3_forHeap(f); 49 | } 50 | void mm_forFreedHeap(V2v f) { 51 | b1_forFreedHeap(f); 52 | b3_forFreedHeap(f); 53 | } 54 | static void mm_freeFreedAndMerge() { 55 | b1_freeFreedAndMerge(); 56 | b3_freeFreedAndMerge(); 57 | } 58 | void mm_dumpHeap(FILE* f) { 59 | b1_dumpHeap(f); 60 | b3_dumpHeap(f); 61 | } 62 | 63 | u64 mm_heapUsed() { 64 | return b1_heapUsed() + b3_heapUsed(); 65 | } 66 | -------------------------------------------------------------------------------- /src/opt/single.c: -------------------------------------------------------------------------------- 1 | #define MM_C 1 2 | #if SINGLE_BUILD 3 | #undef USE_REPLXX 4 | #endif 5 | 6 | #include "../core.h" 7 | #include "../load.c" 8 | #include "../core/tyarr.c" 9 | #include "../core/harr.c" 10 | #include "../core/fillarr.c" 11 | #include "../core/stuff.c" 12 | #include "../core/derv.c" 13 | #include "../core/mm.c" 14 | #include "../core/heap.c" 15 | #include "../utils/hash.c" 16 | #include "../utils/utf.c" 17 | #include "../utils/file.c" 18 | #include "../utils/mut.c" 19 | #include "../utils/each.c" 20 | #include "../utils/bits.c" 21 | #include "../utils/ryu.c" 22 | #include "../builtins/fns.c" 23 | #include "../builtins/sfns.c" 24 | #include "../builtins/select.c" 25 | #include "../builtins/slash.c" 26 | #include "../builtins/group.c" 27 | #include "../builtins/sysfn.c" 28 | #include "../builtins/sort.c" 29 | #include "../builtins/search.c" 30 | #include "../builtins/selfsearch.c" 31 | #include "../builtins/transpose.c" 32 | #include "../builtins/fold.c" 33 | #include "../builtins/scan.c" 34 | #include "../builtins/arithm.c" 35 | #include "../builtins/arithd.c" 36 | #include "../builtins/cmp.c" 37 | #include "../builtins/md1.c" 38 | #include "../builtins/md2.c" 39 | #include "../builtins/internal.c" 40 | #include "../builtins/inverse.c" 41 | #include "../builtins/squeeze.c" 42 | #include "../builtins/compare.c" 43 | #include "../builtins/cells.c" 44 | #include "../builtins/bit.c" 45 | #include "../vm.c" 46 | #include "../ns.c" 47 | #include "../nfns.c" 48 | #include "../rtwrap.c" 49 | #include "../ffi.c" 50 | #include "../jit/nvm.c" 51 | #include "../main.c" 52 | -------------------------------------------------------------------------------- /test/cases/fuzz/cmp.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # tests scalar comparison functions =≠<≤>≥ 3 | helpers ← ⊑•args 4 | noerr ← helpers.shouldNotError # %ALLOW_CATCH 5 | r ← helpers.GetRand@ 6 | fns ← =‿≠‿<‿≤‿>‿≥ 7 | n ← 0÷0 8 | nn ← -n 9 | arrs ← •internal.Squeeze¨ ⟨ 10 | "hello","{⍉⍵}","{⍉𝕩}" 11 | @+0‿1‿2‿126‿127 12 | @+0‿1‿2‿126‿127‿128‿129 13 | @+0‿1‿2‿65533‿65534‿65535 14 | "",↕0 15 | 1‿2‿3 ⋄ ¯1‿0‿1 ⋄ ↕10 ⋄ ¯5+↕10 ⋄ 1‿1.5‿0‿0‿4‿π ⋄ -1‿1.5‿0‿0‿4‿π ⋄ n‿nn‿n‿n‿nn∾(-0‿2‿π)∾1‿2‿5‿0‿0÷0‿0‿3‿5‿0 16 | ¯128‿¯1‿0‿1‿127 17 | ¯32768‿¯1‿0‿1‿32767 18 | ¯2147483648‿¯1‿0‿1‿2147483647 19 | ¯2147483648‿¯1‿0‿1‿2147483648 20 | 63 r.Range 2 21 | 64 r.Range 2 22 | 65 r.Range 2 23 | 90 r.Range 2 24 | ⟩ 25 | atms ← 1‿¯1‿0‿¯0‿∞‿¯∞‿n‿nn‿@‿'l'‿'⍉'‿'𝕩'‿{a⇐1}‿+‿{𝔽} 26 | atms∾↩ -⊸∾ 10⋆↕20 27 | atms∾↩ -⊸∾ ⥊(⥊¯0.9‿¯0.1‿0‿0.1‿0.9+⌜2-↕5) +⌜ 2⋆↕35 28 | atms∾↩ @+⥊128‿65536+⌜2-↕5 29 | 30 | LV ← •internal.ListVariations 31 | V ← •internal.Variation 32 | C ← •internal.ClearRefs 33 | ElType ← •internal.ElType 34 | 35 | NEas ← ((⊑¨∊=‿≠˙) ∨ 8≠·ElType 2⊑¨⊢)⊸/ 36 | 37 | # •Out "AS" 38 | { 39 | fn‿arr‿atm: 40 | C@ ⋄ ∧´(⊏≡¨⊢) {(𝕩 V arr) Fn⎊"err" atm}¨ LV arr?0; 41 | fn‿arr‿atm: 42 | •Out "Fail:" 43 | •Show arr 44 | •Show fn 45 | •Show atm 46 | •Show ⍉(LV arr)≍{(𝕩 V arr) Fn⎊"err" atm}¨ LV arr 47 | ! 0 48 | }¨ NEas⍟noerr ⥊(<⟨⟩)<⊸∾⌜´ fns‿arrs‿atms 49 | 50 | # •Out "AA" 51 | { 52 | fn‿l‿r: 53 | C@ 54 | lr←(⌈´≠¨l‿r)↑¨l‿r 55 | ∧´(⊏≡¨⊢) {fn⎊"err"˝𝕩 V¨ lr}¨ ⥊≍○<⌜´ LV¨lr?0; 56 | 𝕩!0 57 | }¨ (⥊(<⟨⟩)<⊸∾⌜´ ⟨fns⟩ ∾ 2⥊< (0<≠¨)⊸/arrs) ∾ ⥊(<⟨⟩)<⊸∾⌜´ ⟨fns⟩ ∾ 2⥊<⟨"" ⋄ ↕0 ⋄ ⟨⟩⟩ -------------------------------------------------------------------------------- /src/utils/file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "utf.h" 3 | // unless otherwise specified, these assume that paths are strings 4 | 5 | B path_rel(B base, B rel, char* name); // consumes rel; assumes base is a string or bi_N, throws if !isStr(rel) 6 | B path_parent(B path); // consumes; returns the containing directory, with trailing slash 7 | B path_name(B path); // consumes; returns filename from a path 8 | B path_abs(B path); // consumes; returns absolute version of the path; propagates bi_N 9 | 10 | FILE* file_open(B path, char* desc, char* mode); // doesn't consume path 11 | I8Arr* path_bytes(B path); // consumes 12 | B path_chars(B path); // consumes 13 | B path_lines(B path); // consumes 14 | 15 | I8Arr* stream_bytes(FILE* f); 16 | 17 | typedef struct { char* data; bool alloc; } CharBuf; 18 | CharBuf get_chars(B x); // convert x to character data; expects x isn't freed before free_chars call. May error. 19 | void free_chars(CharBuf b); // free the result of the above 20 | 21 | B mmap_file(B path); // consumes 22 | bool dir_create(B path); // doesn't consume 23 | bool path_rename(B old_path, B new_path); // consumes only old_path 24 | bool path_remove(B path); // consumes 25 | 26 | void path_wChars(B path, B x); // consumes path 27 | void path_wBytes(B path, B x); // consumes path 28 | void file_wBytes(FILE* file, char* name, B x); // doesn't consume 29 | 30 | B path_list(B path); // consumes 31 | char path_type(B path); // consumes; errors only if path isn't a string 32 | B path_info(B path, i32 mode); // consumes; mode: 0:created 1:accessed 2:modified 3:size 33 | void cbqn_heapDump(char* name); 34 | -------------------------------------------------------------------------------- /test/cases/fuzz/hashmap.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # •HashMap fuzzing 3 | helpers ← ⊑•args 4 | ro ← helpers.GetRand@ 5 | R ← ro.Range 6 | 7 | err ← ¬helpers.shouldNotError # %ALLOW_CATCH 8 | 9 | Test ← {iter 𝕊 n: 10 | # Global set of keys that might be used 11 | keys ← ⊒⊸(-⊸∾⍟(0<⊣)¨) (R⟜15 R¨ 1-˜1e6⌊∘⋆R⟜0) n 12 | # Which keys are currently set, and count 13 | c ← +´ mask ← 0 = n R 20+R 10 14 | # Value for key if set 15 | vals ← ((R⟜≠⊏⊢)⟜⊢‿÷‿⥊‿↕ {𝕎𝕩}¨ R⟜10) n 16 | 17 | map ← keys •HashMap○(mask⊸/) vals 18 | 19 | # Single operations and checks 20 | _er ← { err ? ! 0∘𝔽⎊1 𝕩⊑keys ; 1 } 21 | Cnt ← {𝕊: ! c ≡ map.Count@ } 22 | Key ← {𝕊: ! (mask/keys) ≡○∧ map.Keys@ } 23 | Val ← {𝕊: ! (mask/vals) ≡○∧ map.Values@ } 24 | Has ← { ! (𝕩⊑mask) ≡ map.Has 𝕩⊑keys } 25 | Get ← { 𝕩⊑mask ? ! (𝕩⊑vals) ≡ map.Get 𝕩⊑keys ; map.Get _er 𝕩 } 26 | Set ← { 𝕨 map.Set˜ 𝕩⊑keys ⋄ vals 𝕨˙⌾(𝕩⊸⊑)↩ ⋄ mask {c+↩¬𝕩⋄1}⌾(𝕩⊸⊑)↩ ⋄ @ } 27 | Del ← { 𝕩⊑mask ? map.Delete 𝕩⊑keys ⋄ mask 0⌾(𝕩⊸⊑)↩ ⋄ c-↩1 ; map.Delete _er 𝕩 } 28 | 29 | Selection ← { (𝕨≤1)◶⟨R·⌊1.5⊸×,𝕨⟩⊸⌊⊸ro.Deal∘≠⊸⊏ 𝕩 } 30 | RandVal ← R∘100 31 | ops ← ⟨Cnt, Has, Get, Del, (R∘n⊑vals˙)Set⊢, RandVal⊸Set⟩ 32 | 33 | # Larger sets: argument is number of ops (sometimes overridden) 34 | FullCheck ← Key∘Val∘Cnt 35 | Deletes ← { Del¨ 𝕩 Selection mask } 36 | Restores ← { ⊏⟜vals⊸(Set¨) 𝕩 Selection ¬mask } 37 | RandomOps ← (R⟜≠⊏⊢)⟜ops {𝕎𝕩}¨ R⟜n 38 | RandomSame ← R∘≠⊸⊑∘ops {𝕎𝕩}¨ R⟜n 39 | opSets ← FullCheck‿Deletes‿Restores‿RandomOps‿RandomSame 40 | ((R⟜≠⊏⊢)⟜opSets {𝕎𝕩}¨ R⟜100) iter 41 | } 42 | 43 | 1e5 Test 1e2 44 | 1e4 Test 1e4 45 | -------------------------------------------------------------------------------- /src/load.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | extern GLOBAL HArr* comps_curr; // global-ish state, valid during compilation; comps_max elements 3 | enum { 4 | comps_path, 5 | comps_name, 6 | comps_args, 7 | comps_src, 8 | comps_re, // HArr of re_* values (re_mode & re_scope have unspecified values) 9 | comps_envPos, 10 | comps_max 11 | }; 12 | enum { 13 | re_compFn, re_compOpts, re_rt, re_glyphs, re_sysNames, re_sysVals, // compiling info 14 | re_map, // •HashMap of cached import results 15 | re_mode, re_scope, // only for repl_exec 16 | re_max 17 | }; 18 | #define COMPS_REF(O,N) O->a[comps_##N] 19 | #define COMPS_CREF(N) COMPS_REF(comps_curr, N) 20 | #define COMPS_ACTIVE() (comps_curr!=NULL) 21 | 22 | extern GLOBAL B def_sysNames, def_sysVals; 23 | B comps_getPrimitives(void); 24 | void comps_getSysvals(B* res); 25 | 26 | typedef struct Block Block; 27 | typedef struct Scope Scope; 28 | NOINLINE B load_fullpath(B path, B name); // doesn't consume 29 | B bqn_explain(B str); // consumes str 30 | B bqn_execFile(B path, B args); // consumes both 31 | B bqn_execFileRe(B path, B args, B re); // consumes path,args 32 | Block* bqn_comp (B str, B state); // consumes both 33 | Block* bqn_compSc (B str, B state, Scope* sc, bool repl); // consumes str,state 34 | Block* bqn_compScc(B str, B state, B re, Scope* sc, bool loose, bool noNS); // consumes str,state 35 | B rebqn_exec (B str, B state, B re); // consumes str,state; runs in a new environment 36 | B repl_exec (B str, B state, B re); // consumes str,state; uses re_mode and re_scope 37 | void init_comp(B* new_re, B* prev_re, B prim, B sys); // doesn't consume; writes re_* compiling info into new_re 38 | -------------------------------------------------------------------------------- /src/nfns.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "h.h" 3 | #include "nfns.h" 4 | 5 | STATIC_GLOBAL u32 nfn_curr; 6 | STATIC_GLOBAL B nfn_list; 7 | 8 | NFnDesc* registerNFn(B name, FC1 c1, FC2 c2) { 9 | u32 id = nfn_curr++; 10 | NFnDesc* r = mm_alloc(sizeof(NFnDesc), t_nfnDesc); 11 | r->id = id; 12 | r->c1 = c1; 13 | r->c2 = c2; 14 | r->name = name; 15 | nfn_list = vec_addN(nfn_list, tag(r,OBJ_TAG)); 16 | return r; 17 | } 18 | B m_nfn(NFnDesc* desc, B obj) { 19 | NFn* r = mm_alloc(sizeof(NFn), t_nfn); 20 | r->id = desc->id; 21 | r->c1 = desc->c1; 22 | r->c2 = desc->c2; 23 | r->obj = obj; 24 | return tag(r,FUN_TAG); 25 | } 26 | B nfn_name(B x) { VTY(x, t_nfn); 27 | return incG(c(NFnDesc,IGetU(nfn_list,c(NFn,x)->id))->name); 28 | } 29 | 30 | DEF_FREE(nfn) { dec(((NFn*)x)->obj); } 31 | void nfn_visit(Value* x) { mm_visit(((NFn*)x)->obj); } 32 | void nfn_print(FILE* f, B x) { fprintsB(f, c(NFnDesc,IGetU(nfn_list,c(NFn,x)->id))->name); } 33 | DEF_FREE(nfnDesc) { fatal("nfnDesc shouldn't be freed!"); } 34 | void nfnDesc_visit(Value* x) { mm_visit(((NFnDesc*)x)->name); } 35 | void nfnDesc_print(FILE* f, B x) { fprintf(f, "(native function description)"); } 36 | 37 | B block_decompose(B x); 38 | void nfn_init(void) { 39 | nfn_list = emptyHVec(); 40 | TIi(t_nfn,freeO) = nfn_freeO; TIi(t_nfnDesc,freeO) = nfnDesc_freeO; 41 | TIi(t_nfn,freeF) = nfn_freeF; TIi(t_nfnDesc,freeF) = nfnDesc_freeF; 42 | TIi(t_nfn,visit) = nfn_visit; TIi(t_nfnDesc,visit) = nfnDesc_visit; 43 | TIi(t_nfn,print) = nfn_print; TIi(t_nfnDesc,print) = nfnDesc_print; 44 | TIi(t_nfn,decompose) = block_decompose; 45 | TIi(t_nfn,byRef) = true; 46 | gc_add_ref(&nfn_list); 47 | } 48 | -------------------------------------------------------------------------------- /licenses/LICENSE-MIT-sort: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010-2019 Christopher Swenson and [others as listed in CONTRIBUTORS.md](CONTRIBUTORS.md) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | 25 | 26 | 27 | contents of the respective CONTRIBUTORS.md: 28 | List of contributors, in alphabetical order: 29 | 30 | - Andrey Astrelin 31 | - Antony Dovgal 32 | - @[Baobaobear](https://github.com/Baobaobear) 33 | - Christopher Swenson 34 | - [@drfie](https://github.com/drfie) 35 | - [@DrMarkS](https://github.com/DrMarkS) 36 | - Emanuel Falkenauer 37 | - Google Inc. 38 | - Haneef Mubarak 39 | - Matthieu Darbois 40 | - Vojtech Fried -------------------------------------------------------------------------------- /src/utils/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "wyhash.h" 3 | #include "talloc.h" 4 | 5 | extern INIT_GLOBAL u64 wy_secret[4]; 6 | 7 | static void bcl(B x, usz ia) { // clean up bitarr tail bits to zero 8 | if (ia&63) { 9 | u64* xp = bitany_ptr(x); 10 | xp[ia>>6]&= (1ULL<<(ia&63)) - 1; 11 | } 12 | } 13 | 14 | static inline f64 normalizeFloat(f64 v) { 15 | return v==v? v+0 : 0.0/0.0; 16 | } 17 | u64 bqn_hashObj(B x, const u64 secret[4]); 18 | static u64 bqn_hash(B x, const u64 secret[4]) { // doesn't consume 19 | u64 h; 20 | if (LIKELY(x.f==x.f)) { 21 | #if TEST_BAD_HASH 22 | return (u64)x.f; 23 | #endif 24 | h = m_f64(x.f+0).u; 25 | } else if (isVal(x)) { 26 | return bqn_hashObj(x, secret); 27 | } else if ((x.u<<1) == (0x7FF8000000000000U<<1)) { 28 | h = secret[1]; 29 | } else { 30 | h = x.u; 31 | } 32 | return wyhash64(secret[0], h); 33 | } 34 | 35 | static u64 bqn_hashP(B x, const u64 secret[4]) { // bqn_hash but never zero 36 | u64 r = bqn_hash(x, secret); 37 | return LIKELY(r)?r:secret[3]; // bias towards secret[3], whatever 38 | } 39 | 40 | 41 | 42 | #define N(X) X##_b2i 43 | #define HT u64 44 | #define KT B 45 | #define H1(K) bqn_hashP(K, wy_secret) 46 | #define H2(K,h1) h1 47 | #define H1R(K,h2) h2 48 | #define EMPTY(S,K) ((S)==0) 49 | #define HDEF 0 50 | #define KEYS 51 | #define EQUAL(A,B) equal(A,B) 52 | #define VALS 53 | #define VT i32 54 | #include "hashmapTemplate.h" 55 | 56 | #define N(X) X##_Sb 57 | #define HT u64 58 | #define KT B 59 | #define H1(K) bqn_hashP(K, wy_secret) 60 | #define H2(K,h1) h1 61 | #define H1R(K,h2) h2 62 | #define EMPTY(S,K) ((S)==0) 63 | #define HDEF 0 64 | #define KEYS 65 | #define EQUAL(A,B) equal(A,B) 66 | #include "hashmapTemplate.h" 67 | -------------------------------------------------------------------------------- /src/builtins/inverse.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #include "../builtins.h" 3 | #include "../nfns.h" 4 | 5 | 6 | STATIC_GLOBAL NFnDesc* fn_invRegDesc; 7 | STATIC_GLOBAL NFnDesc* fn_invSwapDesc; 8 | B fn_invReg_c1 (B t, B x) { B f = nfn_objU(t); return TI(f, fn_im)(f, x); } 9 | B fn_invReg_c2 (B t, B w, B x) { B f = nfn_objU(t); return TI(f, fn_ix)(f, w, x); } 10 | B fn_invSwap_c1(B t, B x) { B f = nfn_objU(t); return TI(f, fn_is)(f, x); } 11 | B fn_invSwap_c2(B t, B w, B x) { B f = nfn_objU(t); return TI(f, fn_iw)(f, w, x); } 12 | 13 | extern GLOBAL B rt_undo; 14 | B undo_c1(Md1D* d, B x) { B f = d->f; 15 | if (isFun(f)) return TI(f, fn_im)(f, x); 16 | SLOW1("!runtime undo", x); 17 | B fi = m1_d(incG(rt_undo), inc(f)); 18 | B r = c1(fi, x); 19 | decG(fi); 20 | return r; 21 | } 22 | B undo_c2(Md1D* d, B w, B x) { B f = d->f; 23 | if (isFun(f)) return TI(f, fn_ix)(f, w, x); 24 | SLOW2("!runtime undo", w, x); 25 | B fi = m1_d(incG(rt_undo), inc(f)); 26 | B r = c2(fi, w, x); 27 | decG(fi); 28 | return r; 29 | } 30 | 31 | B setInvReg_c1 (B t, B x) { rt_invFnReg = x; rt_invFnRegFn = c(Fun,x)->c1; return incG(bi_nativeInvReg); } 32 | B setInvSwap_c1(B t, B x) { rt_invFnSwap = x; rt_invFnSwapFn = c(Fun,x)->c1; return incG(bi_nativeInvSwap); } 33 | B nativeInvReg_c1(B t, B x) { 34 | if (isFun(x)) return m_nfn(fn_invRegDesc, x); 35 | return c1rt(invFnReg, x); 36 | } 37 | B nativeInvSwap_c1(B t, B x) { 38 | if (isFun(x)) return m_nfn(fn_invSwapDesc, x); 39 | return c1rt(invFnSwap, x); 40 | } 41 | 42 | void inverse_init(void) { 43 | fn_invRegDesc = registerNFn(m_c8vec_0("(fn_invReg)"), fn_invReg_c1, fn_invReg_c2); 44 | fn_invSwapDesc = registerNFn(m_c8vec_0("(fn_invSwap)"), fn_invSwap_c1, fn_invSwap_c2); 45 | } 46 | -------------------------------------------------------------------------------- /test/cases/fuzz/random.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # tests (•MakeRand x).Range itself 3 | helpers ← ⊑•args 4 | seed0 ← (helpers.GetRand@).Range 1e18 5 | Range ← (•MakeRand seed0).Range 6 | 7 | ExpectedRangeStep ← { n‿len𝕊depth‿max: 8 | ls ← {𝕊:len Range max}¨↕n 9 | maxGot ← ⌈´⌈´¨ls 10 | ! maxGotn×len÷20) ∨ maxGot=max-1? @; •Out ∾⟨"Maximum ", •Repr max-1, " likely should have been encountered, but max was ", •Repr maxGot, "; n=", •Repr n," len=", •Repr len, " range=", •Repr max⟩} 13 | 14 | # some very very crappy statistics 15 | sumExp ← n×len÷2 16 | sumGot ← +´ +´¨ ls 17 | {𝕊: sumGot ↩ max÷˜ (2÷˜n×len) + sumGot}⍟⊢ max≠0 18 | diff ← sumGot-sumExp 19 | dev ← diff ÷ √12÷˜ n×len 20 | {𝕊: 21 | depth≥4? •Out ∾⟨"Unlikely sum (dev=", •Repr dev, ": expected ", •Repr sumExp, ", got ", •Repr sumGot, "; n=", •Repr n," len=", •Repr len, " range=", •Repr max⟩; 22 | n‿len ExpectedRangeStep ⟨depth+1, max⟩ 23 | }⍟⊢ 2 < |dev 24 | dev 25 | } 26 | 27 | ExpectedRange ← ExpectedRangeStep⟜(0⊸⋈) 28 | 29 | # for whatever reason, the following don't all evaluate to the same number; but they're close enough so ¯\_(ツ)_/¯ 30 | # •Show (+´÷≠) |{𝕊: 100‿1000 ExpectedRange 2 }¨↕10000 31 | # •Show (+´÷≠) |{𝕊: 10‿1000 ExpectedRange 3 }¨↕10000 32 | # •Show (+´÷≠) |{𝕊: 10‿1000 ExpectedRange 1e3}¨↕10000 33 | # •Show (+´÷≠) |{𝕊: 10‿1000 ExpectedRange 1e6}¨↕10000 34 | 35 | am ← 10000 36 | # •Out "general" 37 | ((500+↕20)∾↕10) {(⌊(5×am)÷𝕨⌈1)‿𝕨 ExpectedRange 𝕩}⌜ 3‿10‿100‿1000‿10000‿100000‿1e8‿1e15 ∾ ⥊¯1‿0‿1+⌜2⋆2↓↕34 38 | (↕10) {! ∧´0=𝕩 Range 1}⌜ ↕200 39 | 40 | # •Out "bit boolean" 41 | {⟨5×am,𝕩⟩ ExpectedRange 2}¨ ⥊31‿32‿33×⌜↕100 42 | # •Out "float" 43 | {⟨2×am,𝕩⟩ ExpectedRange 0}¨ ↕100 44 | -------------------------------------------------------------------------------- /src/ns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "vm.h" 3 | 4 | typedef struct NSDesc { 5 | struct Value; 6 | i32 varAm; // number of items in expGIDs (currently equal to sc->varAm/body->varAm) 7 | i32 expGIDs[]; // for each variable; -1 if not exported, otherwise a gid 8 | } NSDesc; 9 | typedef struct NS { 10 | struct Value; 11 | NSDesc* desc; 12 | Scope* sc; 13 | } NS; 14 | 15 | void m_nsDesc(Body* body, bool imm, u8 ty, i32 vam, B nameList, B varIDs, B exported); // doesn't consume nameList 16 | B m_ns(Scope* sc, NSDesc* desc); // consumes both 17 | 18 | B ns_getU(B ns, i32 gid); // doesn't consume, doesn't increment result 19 | B ns_qgetU(B ns, i32 gid); // ns_getU but return bi_N on fail 20 | B ns_getNU(B ns, B name, bool thrEmpty); // doesn't consume anything, doesn't increment result; returns bi_N if doesn't exist and !thrEmpty 21 | B ns_getC(B ns, char* name); // get namespace field by C string; returns bi_N if doesn't exist, otherwise doesn't increment result like ns_getU 22 | void ns_set(B ns, B name, B val); // consumes val 23 | 24 | i32 pos2gid(Body* body, i32 pos); // converts a variable position to a gid; errors on special name variables 25 | i32 str2gid(B s); // doesn't consume 26 | i32 str2gidQ(B s); // doesn't consume 27 | B gid2str(i32 n); // returns unowned object 28 | 29 | 30 | #define m_nnsDesc(...) ({ char* names_[] = {__VA_ARGS__}; m_nnsDescF(sizeof(names_)/sizeof(char*), names_); }) 31 | #define m_nns(desc, ...) ({ B vals_[] = {__VA_ARGS__}; m_nnsF(desc, sizeof(vals_)/sizeof(B), vals_); }) 32 | 33 | Body* m_nnsDescF(i32 n, char** names); // adds the result as a permanent GC root 34 | B m_nnsF(Body* desc, i32 n, B* vals); 35 | i32 nns_pos(Body* desc, B name); // consumes name; returns an index in sc->vars for a given variable (exported or local) 36 | -------------------------------------------------------------------------------- /src/utils/valgrind.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | static void pst(char* msg) { 5 | VALGRIND_PRINTF_BACKTRACE("%s", msg); 6 | } 7 | 8 | static u64 vg_getDefined_u64(u64 x) { // for each bit, returns whether it is defined 9 | u64 r; 10 | i32 v = VALGRIND_GET_VBITS(&x, &r, 8); 11 | if(v==0) return ~0ULL; // don't do weird stuff if not on valgrind 12 | if (v!=1) fatal("unexpected VALGRIND_GET_VBITS result"); 13 | return ~r; 14 | } 15 | static u64 vg_withDefined_u64(u64 x, u64 where) { 16 | where = ~where; 17 | i32 v = VALGRIND_SET_VBITS(&x, &where, 8); 18 | if (v>1) fatal("unexpected VALGRIND_SET_VBITS result"); 19 | return x; 20 | } 21 | static u64 vg_undef_u64(u64 x) { 22 | return vg_withDefined_u64(x, 0); 23 | } 24 | static u64 vg_def_u64(u64 x) { 25 | return vg_withDefined_u64(x, ~0ULL); 26 | } 27 | static u64 vg_withBit_u64(u64 r, i32 i, bool val) { 28 | return (r & ~(1ULL<p = p; 8 | r->a = ptr; 9 | return (Arr*)r; 10 | } 11 | static Arr* TP(,arr_slice) (B x, usz s, usz ia) { return TP(m_,slice) (a(x), ((TEl*)c(TyArr,x)->a)+s, ia); } 12 | static Arr* TP(,slice_slice) (B x, usz s, usz ia) { Arr* p = ptr_inc(c(Slice,x)->p); Arr* r = TP(m_,slice) (p, ((TEl*)c(TySlice,x)->a)+s, ia); decG(x); return r; } 13 | 14 | static B TP(,arr_get) (Arr* x, usz n) { assert(PTY(x)==T_ARR && na)[n]); } 15 | static B TP(,slice_get) (Arr* x, usz n) { assert(PTY(x)==T_SLICE && na)[n]); } 16 | static bool TP(,arr_canStore) (B x) { return TP(q_,) (x); } 17 | 18 | static void TP(,arr_init)() { 19 | TIi(T_ARR,get) = TP(,arr_get); TIi(T_SLICE,get) = TP(,slice_get); 20 | TIi(T_ARR,getU) = TP(,arr_get); TIi(T_SLICE,getU) = TP(,slice_get); 21 | TIi(T_ARR,slice) = TP(,arr_slice); TIi(T_SLICE,slice) = TP(,slice_slice); 22 | TIi(T_ARR,freeO) = tyarr_freeO; TIi(T_SLICE,freeO) = slice_freeO; // typed array slice frees calling the generic function is relied on by mmap & •bit._cast 23 | TIi(T_ARR,freeF) = tyarr_freeF; TIi(T_SLICE,freeF) = slice_freeF; 24 | TIi(T_ARR,visit) = arr_visit; TIi(T_SLICE,visit) = slice_visit; 25 | TIi(T_ARR,print) = farr_print; TIi(T_SLICE,print) = farr_print; 26 | TIi(T_ARR,isArr) = true; TIi(T_SLICE,isArr) = true; 27 | TIi(T_ARR,arrD1) = true; TIi(T_SLICE,arrD1) = true; 28 | TIi(T_ARR,elType) = TP(el_,); TIi(T_SLICE,elType) = TP(el_,); 29 | TIi(T_ARR,canStore) = TP(,arr_canStore); 30 | } 31 | 32 | #undef TEl 33 | #undef TU 34 | #undef TP 35 | -------------------------------------------------------------------------------- /src/utils/talloc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct TAlloc { 4 | struct Value; 5 | u8 data[]; 6 | } TAlloc; 7 | #define TOFF offsetof(TAlloc, data) 8 | #define TALLOCP(T,AM) ((T*) ((TAlloc*)mm_alloc(TOFF + (AM)*sizeof(T) + 8, t_talloc))->data) // +8 so mm is happy 9 | #define TALLOC(T,N,AM) T* N = TALLOCP(T,AM); 10 | #define TOBJ(N) (void*)((u8*)(N) - TOFF) 11 | #define TFREE(N) mm_free((Value*)TOBJ(N)); 12 | #define TREALLOC(N, AM) talloc_realloc(TOBJ(N), AM) 13 | #define TSIZE(N) (mm_sizeUsable(TOBJ(N))-TOFF) 14 | static inline void* talloc_realloc(TAlloc* t, u64 am) { // TODO maybe shouldn't be inline? 15 | u64 stored = mm_sizeUsable((Value*)t)-TOFF; 16 | if (stored > am) { 17 | #if VERIFY_TAIL 18 | tailVerifySetMinTallocSize(t, am); 19 | #endif 20 | return t->data; 21 | } 22 | TALLOC(u8,r,am); 23 | memcpy(r, t->data, stored); 24 | mm_free((Value*)t); 25 | return r; 26 | } 27 | 28 | typedef struct TStack { 29 | struct Value; 30 | usz size; 31 | usz cap; 32 | u8 data[]; 33 | } TStack; 34 | #define TSALLOC(T,N,I) usz N##_dc=(I); u32 N##_e=sizeof(T); TStack* N##_o = (TStack*)mm_alloc(sizeof(TStack)+N##_e*N##_dc, t_temp); N##_o->size=0; N##_o->cap=N##_dc; T* N = (T*)N##_o->data; 35 | #define TSFREE(N) mm_free((Value*)N##_o); 36 | #define TSUPD(N,AM) { N##_o = ts_e(N##_o, N##_e, AM); N = (void*)N##_o->data; } 37 | #define TSADD(N,X) ({ if (N##_o->size==N##_o->cap) TSUPD(N, 1); N[N##_o->size++] = X; }) 38 | #define TSADDA(N,P,AM) { u64 n_=(AM); if(N##_o->size+n_>N##_o->cap) TSUPD(N, n_); memcpy(N+N##_o->size,P,n_*N##_e); N##_o->size+= n_; } 39 | #define TSADDAU(N,AM) { u64 n_=(AM); if(N##_o->size+n_>N##_o->cap) TSUPD(N, n_); N##_o->size+= n_; } 40 | #define TSFREEP(N) mm_free((void*)RFLD(N, TStack, data)); 41 | #define TSSIZE(N) (N##_o->size) 42 | TStack* ts_e(TStack* o, u32 elsz, u64 am); 43 | 44 | #define ARBOBJ(SZ) ((TAlloc*)mm_alloc(sizeof(TAlloc)+(SZ), t_arbObj)) 45 | -------------------------------------------------------------------------------- /test/cases/patterns.bqn: -------------------------------------------------------------------------------- 1 | %DEF k _k ← {𝔽○•internal.Keep} 2 | %DEF rand r ← (⊑•args).GetRand@ 3 | 4 | # weird "functions" 5 | 4 10´⟨⟩ %% 4 6 | 4 10˝⟨⟩ %% 4 7 | 4 10`⟨⟩ %% ⟨⟩ 8 | 4 10´↕0 %% 4 9 | 4 10˝↕0 %% 4 10 | 4 10`↕0 %% ↕0 11 | 4 {˜}´↕0 %% 4 12 | 4 {˜}˝↕0 %% 4 13 | 4 {∘}`↕0 %% ↕0 14 | {∘}¨↕0 %% ⟨⟩ 15 | {˜}¨˜↕0 %% ⟨⟩ 16 | !"Calling a modifier" % 4 {˜}´↕1 17 | !"Calling a modifier" % 4 {˜}˝↕1 18 | !"Calling a modifier" % 4 {∘}`↕1 19 | !"Calling a modifier" % {∘}¨↕1 20 | !"Cannot call a modifier" % {˜}¨˜↕1 21 | 4 {˜}˙´↕1 %% ˜ 22 | 4 {˜}˙˝↕1 %% ˜ 23 | 4 {∘}˙`↕1 %% ⟨∘⟩ 24 | {∘}˙¨↕1 %% ⟨∘⟩ 25 | {˜}˙¨˜↕1 %% ⟨˜⟩ 26 | 4 "ab"`↕4 %% 4⥊<"ab" 27 | !"Calling a modifier" % {∘}⌜˜ ↕10 28 | {∘}˙⌜˜ ↕10 %% 10‿10⥊⟨∘⟩ 29 | "ab"` "hello" %% 'h'∾4⥊<"ab" 30 | 31 | # ˝ plus ˘ 32 | %USE rand ⋄ %USE k ⋄ (+˝˘ !∘≡ +˝_k˘) 1000000‿3 r.Range 0 33 | %USE rand ⋄ %USE k ⋄ (-˜˝ !∘≡ -˜_k˝) 10 r.Range 1e5 34 | %USE rand ⋄ %USE k ⋄ (-˜˝˘ !∘≡ -˜_k˝˘) 3↕10 r.Range 1e5 35 | 36 | # +` 37 | %USE rand ⋄ %USE k ⋄ {𝕊: (+` !∘≡ +_k`) 1e3 r.Range 0}¨↕1000 38 | %USE rand ⋄ %USE k ⋄ {𝕊: ( r.Range 0) (+` !∘≡ +_k`) 1e3 r.Range 0}¨↕1000 39 | 40 | # <` 41 | %USE k ⋄ (<` !∘≡ <_k`)¨ (1000⊸⥊)¨⊸∾ "Ab"⊸•internal.Variation¨ "111000"‿"1111000"‿"0111000"-'0' 42 | 43 | # ≠` 44 | %USE rand ⋄ {𝕤⋄b←"Ab"•internal.Variation a←"Ai32"•internal.Variation 1000 r.Range 2 ⋄ ! (≠`a)≡≠`b}¨↕1000 45 | %USE rand ⋄ {𝕤⋄b←"Ab"•internal.Variation a←"Ai32"•internal.Variation 1000 r.Range 2 ⋄ ! (0≠`a)≡0≠`b}¨↕1000 46 | %USE rand ⋄ {𝕤⋄b←"Ab"•internal.Variation a←"Ai32"•internal.Variation 1000 r.Range 2 ⋄ ! (1≠`a)≡1≠`b}¨↕1000 47 | 48 | # ∨´ & ∧´ 49 | {n←𝕩 ⋄ ! (1+n) ≡ +´¬{∨´(-𝕩)↑1∾n↑0}¨↕400}¨↕200 # ∨´ with a single 1; %SLOW 50 | {n←𝕩 ⋄ ! (1+n) ≡ +´¬ {¬∧´¬(-𝕩)↑1∾n↑0}¨↕400}¨↕200 # ∧´ with a single 1; %SLOW 51 | {!¬ ∨´𝕩⥊0}¨↕1000 # ∨´ zeroes 52 | {!¬ ∧´𝕩⥊0}¨1+↕1000 # ∧´ zeroes 53 | {! ∨´𝕩⥊1}¨1+↕1000 # ∨´ ones 54 | {! ∧´𝕩⥊1}¨↕1000 # ∧´ ones 55 | 56 | a←0↑∧1‿10⥊0 ⋄ a⍋↕10 ⋄ •internal.Squeeze a %% 0‿10⥊0 # sorted integer squeeze path on empty array 57 | -------------------------------------------------------------------------------- /src/builtins/sort.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #include "../utils/talloc.h" 3 | 4 | // Defines Sort, Grade, isSortedUp/Down, and Bins 5 | 6 | static bool bit_isSorted(void* data, u64 down, usz n) { 7 | ux xia = n+1; 8 | u64* xp = data; 9 | u64 i = bit_find(xp, xia, !down); 10 | usz iw = i/64; 11 | u64 m = ~(u64)0; 12 | u64 mid = xp[iw]; 13 | u64 d = (down? ~mid : mid) ^ (m<<(i%64)); 14 | if (iw == xia/64) return (d &~ (m<<(xia%64))) == 0; 15 | if (d) return 0; 16 | usz o = iw + 1; 17 | usz l = xia - 64*o; 18 | return (bit_find(xp+o, l, down) == l); 19 | } 20 | 21 | #if SINGELI 22 | #define SINGELI_FILE bins 23 | #include "../utils/includeSingeli.h" 24 | #endif 25 | 26 | #if SINGELI_SIMD 27 | typedef bool (*IsSortedFn)(void*, u64, usz); 28 | INIT_GLOBAL IsSortedFn is_sorted[el_B]; 29 | #define SINGELI_FILE sort 30 | #include "../utils/includeSingeli.h" 31 | #endif 32 | 33 | #define CAT0(A,B) A##_##B 34 | #define CAT(A,B) CAT0(A,B) 35 | typedef struct BI32p { B k; i32 v; } BI32p; 36 | typedef struct I32I32p { i32 k; i32 v; } I32I32p; 37 | 38 | static NOINLINE void generic_grade(B x, usz ia, B r, i32* rp, void (*fn)(BI32p*, size_t)) { 39 | TALLOC(BI32p, tmp, ia); 40 | SGetU(x) 41 | for (usz i = 0; i < ia; i++) { 42 | tmp[i].v = i; 43 | tmp[i].k = GetU(x,i); 44 | } 45 | fn(tmp, ia); 46 | vfor (usz i = 0; i < ia; i++) rp[i] = tmp[i].v; 47 | TFREE(tmp); 48 | } 49 | static bool q_nan(B x) { 50 | assert(isNum(x)); 51 | double f = o2fG(x); 52 | return f!=f; 53 | } 54 | 55 | #define GRADE_UD(U,D) U 56 | #include "grade.h" 57 | #define GRADE_UD(U,D) D 58 | #include "grade.h" 59 | 60 | GLOBAL B int2x[2]; // ⟨0‿1 ⋄ 1‿0⟩ as i8 arrays 61 | void sort_init() { 62 | #if SINGELI_SIMD 63 | is_sorted[el_bit] = bit_isSorted; 64 | for (ux i = el_i8; i <= el_c32; i++) is_sorted[i] = si_is_sorted[i-el_i8]; 65 | #endif 66 | 67 | { i8* p; B a=m_i8arrv(&p, 2); p[0]=0; p[1]=1; int2x[0] = a; gc_add(a); } 68 | { i8* p; B a=m_i8arrv(&p, 2); p[0]=1; p[1]=0; int2x[1] = a; gc_add(a); } 69 | } -------------------------------------------------------------------------------- /test/cases/fuzz/select-cells.bqn: -------------------------------------------------------------------------------- 1 | %DEF var V←•internal.Variation ⋄ LV←•internal.ListVariations ⋄ CLR←•internal.ClearRefs 2 | %DEF tvar %USE var ⋄ _tvar ← {F _𝕣 x: (CLR@) ⊢ {F 𝕩 V x}¨ LV 𝕩; w F _𝕣 x: (CLR@) ⊢ (LV 𝕨) {(𝕨 V w) F 𝕩 V x}⌜ LV 𝕩} 3 | %DEF eqvar ⟨_eqvar⟩←⊑•args 4 | %DEF eqvarv ⟨_eqvarv_⟩←⊑•args 5 | %DEF rand r ← (⊑•args).GetRand@ 6 | 7 | ( 8 | %USE rand ⋄ %USE eqvarv 9 | Test ← { 𝕊: 10 | x ← (2 r.Range 50) r.Range 100 11 | w ← {0: ↕0; (1+r.Range 50) r.Range 𝕩} 1⊑≢x 12 | w {𝕨⊸⊏˘ 𝕩}_eqvarv_"" x 13 | } 14 | 15 | Test¨ ↕1000 16 | ) 17 | 18 | ( 19 | %USE rand 20 | Test ← {𝕊 gen: 21 | { 𝕊 cam: 22 | { 23 | i ← r.Range∘3◶⟨⊢, ¯1⊸-, {𝕩 - (1+2×𝕩) × 2 r.Range˜≠𝕩}⟩ 𝕨 r.Range 𝕩 24 | ! (i⊸⊏˘ ≡ i⊸⊏○•internal.Keep˘) Gen cam‿𝕩 25 | }⌜˜ 1↓⍷(↕20)∾⌊(√2)⋆↕16 26 | }¨ 1‿6‿100 27 | } 28 | 29 | Test {{0 + 𝕩 r.Range 100}} 30 | Test {{0 + 𝕩 r.Range 1000}} 31 | Test {{0 + 𝕩 r.Range 1e9}} 32 | Test {{0 + 𝕩 r.Range 0}} 33 | 34 | Test {{@ + 𝕩 r.Range 100}} 35 | Test {{@ + 𝕩 r.Range 1000}} 36 | Test {{@ + 𝕩 r.Range 1114111}} 37 | Test {{⊑⟜"foo"‿@¨ 𝕩 r.Range 2}} 38 | ) 39 | 40 | 41 | ( 42 | %USE rand ⋄ %USE var 43 | F ← {𝕨⊸⊏˘ 𝕩} 44 | { 45 | 𝕩+↩ 1 46 | is ← 𝕨 r.Range 𝕩 47 | n ← r.Range 200 48 | d ← n‿𝕩 r.Range 2 49 | is‿n‿𝕩 ! 1=≠⍷ ⟨is F d, is F "Ai8"V d⟩ ∾ {𝕩=2? ⟨("Ai8"V is) F d⟩; ⟨⟩}𝕩 50 | }⌜˜ ↕10 51 | ) 52 | 53 | ( 54 | %USE rand ⋄ %USE var 55 | F ← {𝕨⊸⊏˘ 𝕩}⎊'e' 56 | { 57 | 𝕩+↩ 1 58 | 𝕨+↩ 1 59 | is ← 𝕨⥊𝕩 60 | n ← 1+ r.Range 200 61 | d ← n‿𝕩 r.Range 2 62 | 63 | 𝕨‿𝕩‿n! ∧´ 'e'⊸≡¨ ⟨is F d, is F "Ai8"V d⟩ ∾ {𝕩=2? ⟨("Ai8"V is) F d⟩; ⟨⟩}𝕩 64 | }⌜˜ ↕10 65 | ) 66 | 67 | ( 68 | %USE rand ⋄ %USE var 69 | F ← {𝕨⊸⊏˘ 𝕩}⎊'e' 70 | 71 | {𝕊 inds: 72 | csz ← ≠⊑inds 73 | inds { 74 | ⟨csz, 𝕨, ≢𝕩⟩ ! 1=≠⍷ ⥊ (V⟜𝕨¨ (∧´𝕨≤1)◶⟨⋈, "Ab"⊸⋈⟩ "Ai8") F⌜ V⟜𝕩¨ "Ab"‿"Ai8"‿"Ai32" 75 | }⌜ {𝕩‿csz r.Range 2}¨ (↕8)∾⥊0‿1+⌜3↓2⋆↕9 76 | }¨ ⟨⥊↕2‿2⟩ ∾ {⟨↕𝕩, r.Range˜𝕩⟩}¨ 1‿3‿4‿5‿8‿9‿16 77 | ) -------------------------------------------------------------------------------- /src/singeli/src/group.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | include './vecfold' 3 | include './mask' 4 | include './accumulator' 5 | 6 | def __lt{a:V=[_]_, b if knum{b}} = a < V**b 7 | def __eq{a:V=[_]_, b if knum{b}} = a == V**b 8 | 9 | def group_statistics{T} = { 10 | def usz_accumulator = count_accumulator{usz, ...} 11 | def max_accumulator = assoc_accumulator{__max, ..., -1} 12 | 13 | def {types, acc_gen, ops} = each{tup, 14 | tup{u8, any_accumulator, {_,w} => w < -1}, # bad 15 | tup{usz, usz_accumulator, {_,w} => w == -1}, # neg 16 | tup{u8, all_accumulator, {p,w} => p <= w }, # sort 17 | tup{usz, usz_accumulator, {p,w} => p != w }, # change 18 | tup{T, max_accumulator, {_,w} => w } # max 19 | } 20 | 21 | fn group_statistics{T}(w:*void, xn:usz, outs:each{__pnt,types}) : void = { 22 | def w = *T~~w 23 | def accs = if (has_simd) { 24 | def bulk = arch_defvw{}/width{T} 25 | def V = [bulk]T 26 | def VU = ty_u{V} 27 | 28 | def unr = 2 29 | def accs = each{{a,T} => a{unr, if (quality{T}=='u') VU else V}, acc_gen, types} 30 | 31 | prev_v:V = load{V, w-1} | V~~mask_of_first{V, 1} 32 | @for_mu{bulk, unr, mu_extra{...accs}}(curr_vs in tup{V,w}, next_vs in tup{V,w+bulk-1}, M in 'm' over xn) { 33 | def prev_es = shiftright{tup{prev_v}, next_vs} 34 | each{{a, F} => { 35 | a{'acc', M, each{F, prev_es, curr_vs}} 36 | }, accs, ops} 37 | prev_v = select{next_vs,-1} 38 | } 39 | accs 40 | } else { 41 | p:T = -1 42 | def accs = each{{a,T} => a{'!', T}, acc_gen, types} 43 | @for (c in w over xn) { 44 | each{{a, F} => a{'acc', F{p, c}}, accs, ops} 45 | p = c 46 | } 47 | accs 48 | } 49 | def results = each{{a} => a{'vec_result'}, accs} 50 | each{{out:*T, r} => store{out, 0, promote{T,r}}, outs, results} 51 | } 52 | group_statistics{T} 53 | } 54 | 55 | export{'si_group_statistics_i8', group_statistics{i8}} 56 | export{'si_group_statistics_i16', group_statistics{i16}} 57 | export{'si_group_statistics_i32', group_statistics{i32}} 58 | -------------------------------------------------------------------------------- /src/windows/utf16.h: -------------------------------------------------------------------------------- 1 | #include "../utils/talloc.h" 2 | #include 3 | 4 | static u64 utf16lenB(B x) { // doesn't consume 5 | assert(isArr(x)); 6 | SGetU(x) 7 | usz ia = IA(x); 8 | u64 res = 0; 9 | for (usz i = 0; i < ia; ++i) { 10 | u32 c = o2c(GetU(x,i)); 11 | res+= 1+(c > 0xFFFF); 12 | } 13 | return res; 14 | } 15 | 16 | FORCE_INLINE void utf16_w(WCHAR** buf_i, u32 c) 17 | { 18 | WCHAR* buf = *buf_i; 19 | if (c<=0xFFFF) { 20 | *buf++ = c; 21 | } else { 22 | assert(c <= 0x10FFFF); 23 | *buf++ = ((c-0x10000) >> 10)+0xD800; 24 | *buf++ = ((c-0x10000)&0x3FF)+0xDC00; 25 | } 26 | *buf_i = buf; 27 | } 28 | 29 | static void toUTF16(B x, WCHAR* p) { 30 | SGetU(x) 31 | usz ia = IA(x); 32 | for (u64 i = 0; i < ia; ++i) utf16_w(&p, o2cG(GetU(x,i))); 33 | } 34 | 35 | static B utf16Decode(const WCHAR* s, i64 len) { 36 | #define UTF16_MASK 0xFC00 37 | #define UTF16_IS_HI(WC) ((UTF16_MASK&(WC))==0xD800) /* 0xD800..0xDBFF */ 38 | #define UTF16_IS_LO(WC) ((UTF16_MASK&(WC))==0xDC00) /* 0xDC00..0xDFFF */ 39 | #define UTF16_SURROGATE(HI, LO) (0x10000+(((HI)-0xD800) << 10)+((LO)-0xDC00)) 40 | u64 sz = 0; 41 | for (i64 j = 0; ; ++j) { 42 | if (j>=len) { 43 | if (j!=len) assert(0); 44 | break; 45 | } 46 | if (UTF16_IS_HI(s[j]) && j+11) ⟨val⟩ 35 | expg ← (∧´⊑=⊢)◶3‿⊑ { 36 | 1=𝕩? 1; 37 | 2=𝕩? 2; 38 | 3 39 | }∘•type¨ vals 40 | 41 | tOut‿tIn ← { 42 | expg=1? 43 | msk ← ∧´ {((¬int) ∨ 𝕩≡⌊𝕩) ∧ 1∾˜ (≥⟜nmin∧≤⟜nmax)𝕩}¨ vals 44 | ⟨1↑msk/ntn ⋄ "Ah"‿"Af"∾msk/ntv⟩; 45 | expg=2? 46 | msk ← ∧´ {cmax>𝕩-@}¨ vals 47 | ⟨1↑msk/ctn ⋄ "Ah"‿"Af"∾msk/ctv⟩; 48 | expg=3? 49 | ⟨"h"‿"fill" ⋄ "Ah"‿"Af"⟩ 50 | } 51 | tIn ↩ {'S'∾1↓𝕩}¨⊸∾ ∾⟜"Inc"¨⊸∾ tIn 52 | 53 | { 54 | arrv ← 𝕩 Variation arr 55 | arrv0 ← Unshare arrv 56 | sq ← Squeeze arrv 57 | sqt ← Type sq 58 | sqt↩{ 59 | "arr"≡¯3↑sqt? ¯3↓sqt; 60 | !"slice"≡¯5↑sqt ⋄ ¯5↓sqt 61 | } 62 | ¬ (arrv0 EEqual arr) ∧ (arrv0 EEqual sq) ∧ (⊑(&1 | tail -2 || exit 11 | build/build f='-DWARN_SLOW' c && ./BQN -p 2+2 2> /dev/null || exit 12 | build/build f='-DMM=0 -DENABLE_GC=0' c && ./BQN -p 2+2 || exit 13 | build/build f='-DMM=1' c && ./BQN -p 2+2 || exit 14 | build/build f='-DMM=2' debug c && ./BQN "$1/test/this.bqn" || exit 15 | build/build usz=64 debug singeli arch=generic c && ./BQN "$1/test/this.bqn" || exit 16 | build/build f='-DTYPED_ARITH=0' c && ./BQN -p 2+2 || exit 17 | build/build f='-DFAKE_RUNTIME' c && ./BQN -p 2+2 || exit 18 | build/build f='-DALL_R0' c && ./BQN -p 2+2 || exit 19 | build/build f='-DALL_R1' c && ./BQN -p 2+2 || exit 20 | build/build f='-DALL_R0 -DALL_R1' c && ./BQN -p 2+2 || exit 21 | build/build f='-DSFNS_FILLS=0' c && ./BQN -p 2+2 || exit 22 | build/build f='-DFORMATTER=0' c && ./BQN -p 2+2 || exit 23 | build/build f='-DFFI_CHECKS=0' c && ./BQN -p 2+2 || exit 24 | build/build f='-DDONT_FREE' c && ./BQN -p 2+2 || exit 25 | build/build f='-DOBJ_COUNTER' c && ./BQN -p 2+2 || exit 26 | build/build f='-DNO_RT' c && ./BQN -p 2+2 || exit 27 | build/build f='-DNATIVE_COMPILER' c && ./BQN -p 2+2 || exit 28 | build/build f='-DNATIVE_COMPILER -DONLY_NATIVE_COMP -DFORMATTER=0 -DNO_RT -DNO_EXPLAIN' c && ./BQN -p 2+2 || exit 29 | build/build f='-DGC_LOG_DETAILED' c && ./BQN -p 2+2 || exit 30 | build/build f='-DUSE_PERF' c && ./BQN -p 2+2 || exit 31 | build/build f='-DREPL_INTERRUPT=0' c && ./BQN -p 2+2 || exit 32 | build/build f='-DREPL_INTERRUPT=1' c && ./BQN -p 2+2 || exit 33 | build/build FFI=0 c && ./BQN -p 2+2 || exit 34 | build/obj2/for_build5 test/precompiled.bqn "$1" "$PATH" '2+2' || exit 35 | build/build f='-DNO_RT -DPRECOMP' c && ./BQN || exit 36 | -------------------------------------------------------------------------------- /src/utils/calls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #define M1C1(M,F, X) m1c1_unsafe(M##_c1, bi_##F, X) 5 | #define M1C2(M,F,W,X) m1c2_unsafe(M##_c2, bi_##F, W, X) 6 | static inline B m1c1_unsafe(D1C1 m, B f, B x) { Md1D d; d.f=f; return m(&d, x); } 7 | static inline B m1c2_unsafe(D1C2 m, B f, B w, B x) { Md1D d; d.f=f; return m(&d, w, x); } 8 | 9 | typedef void (*CmpAAFn)(u64*, void*, void*, u64); 10 | typedef void (*CmpASFn)(u64*, void*, u64, u64); 11 | #define CMP_DEF(F, S) extern INIT_GLOBAL Cmp##S##Fn cmp_fns_##F##S[]; 12 | CMP_DEF(eq, AS); CMP_DEF(eq, AA); 13 | CMP_DEF(ne, AS); CMP_DEF(ne, AA); 14 | CMP_DEF(gt, AS); CMP_DEF(gt, AA); 15 | CMP_DEF(ge, AS); CMP_DEF(ge, AA); 16 | CMP_DEF(lt, AS); 17 | CMP_DEF(le, AS); 18 | 19 | // will write up to 8×⌈len÷64 bytes to WHERE, i.e. whole u64-s; LEN must not be 0 20 | #define CMP_AA_FN(FN, ELT) cmp_fns_##FN##AA[ELT] 21 | #define CMP_AS_FN(FN, ELT) cmp_fns_##FN##AS[ELT] 22 | 23 | #define CMP_AA_CALL(FN, WHERE, WP, XP, LEN) FN(WHERE, WP, XP, LEN) 24 | #define CMP_AS_CALL(FN, WHERE, WP, X, LEN) FN(WHERE, WP, (X).u, LEN) 25 | 26 | #define CMP_AA_IMM(FN, ELT, WHERE, WP, XP, LEN) CMP_AA_CALL(CMP_AA_FN(FN, ELT), WHERE, WP, XP, LEN) 27 | #define CMP_AS_IMM(FN, ELT, WHERE, WP, X, LEN) CMP_AS_CALL(CMP_AS_FN(FN, ELT), WHERE, WP, X, LEN) 28 | 29 | typedef bool (*MatchFn)(void* a, void* b, u64 l, u64 data); 30 | extern INIT_GLOBAL MatchFn matchFns[]; 31 | extern INIT_GLOBAL MatchFn matchFnsR[]; 32 | extern INIT_GLOBAL u8 const matchFnData[]; 33 | typedef struct { MatchFn fn; u8 data; } MatchFnObj; 34 | #define MATCH_GET( W_ELT, X_ELT) ({ u8 mfn_i_ = ((W_ELT)*8 + (X_ELT)); (MatchFnObj){.fn=matchFns [mfn_i_], .data=matchFnData[mfn_i_]}; }) 35 | #define MATCHR_GET(W_ELT, X_ELT) ({ u8 mfn_i_ = ((W_ELT)*8 + (X_ELT)); (MatchFnObj){.fn=matchFnsR[mfn_i_], .data=matchFnData[mfn_i_]}; }) 36 | #define MATCH_CALL(FN, W, X, L) (FN).fn(W, X, L, (FN).data) // check if L elements starting at a and b match; assumes L≥1 37 | 38 | typedef bool (*RangeFn)(void* xp, i64* res, u64 len); // assumes len≥1; if x has non-integers or values with absolute value >2⋆53, will return 0 or report min<-2⋆53 or max>2⋆53; else, writes min,max in res and returns 1 39 | extern INIT_GLOBAL RangeFn getRange_fns[el_f64+1]; // limited to ≤el_f64 40 | 41 | typedef void (*BitSelFn)(void*,u64*,u64,u64,u64); 42 | extern INIT_GLOBAL BitSelFn* bitselFns; 43 | -------------------------------------------------------------------------------- /src/utils/includeSingeli.h: -------------------------------------------------------------------------------- 1 | #if !defined(SI_C_DEFS) 2 | #define SI_C_DEFS 3 | 4 | #if EXACTLY_GCC && (defined(__x86_64) || defined(__amd64__)) // old gcc versions don't define some smaller-size loads/stores 5 | #if __GNUC__<=10 6 | #include 7 | static __m128i custom_loadu_si32(void* p) { return (__m128i) _mm_load_ss(p); } 8 | static __m128i custom_loadu_si16(void* p) { return _mm_insert_epi16(_mm_setzero_si128(), *(i16*)p, 0); } 9 | static void custom_storeu_si32(void* p, __m128i x) { _mm_store_ss(p, _mm_castsi128_ps(x)); } 10 | static void custom_storeu_si16(void* p, __m128i x) { *(i16*)p = _mm_extract_epi16(x, 0); } 11 | #define _mm_loadu_si16 custom_loadu_si16 12 | #define _mm_loadu_si32 custom_loadu_si32 13 | #define _mm_storeu_si16 custom_storeu_si16 14 | #define _mm_storeu_si32 custom_storeu_si32 15 | #if __GNUC__<=9 16 | #define _mm256_zextsi128_si256(X) _mm256_setr_m128i(X, _mm_setzero_si128()) 17 | #endif 18 | #endif 19 | #endif 20 | #if defined(__aarch64__) 21 | #define unpacked_vqtbl2q_s8(A,B,I) vqtbl2q_s8((int8x16x2_t){A,B}, I) 22 | #define unpacked_vqtbl3q_s8(A,B,C,I) vqtbl3q_s8((int8x16x3_t){A,B,C}, I) 23 | #define unpacked_vqtbl4q_s8(A,B,C,D,I) vqtbl4q_s8((int8x16x4_t){A,B,C,D}, I) 24 | #define unpacked_vqtbl2q_u8(A,B,I) vqtbl2q_u8((uint8x16x2_t){A,B}, I) 25 | #define unpacked_vqtbl3q_u8(A,B,C,I) vqtbl3q_u8((uint8x16x3_t){A,B,C}, I) 26 | #define unpacked_vqtbl4q_u8(A,B,C,D,I) vqtbl4q_u8((uint8x16x4_t){A,B,C,D}, I) 27 | #endif 28 | 29 | #if USE_VALGRIND 30 | #if defined(__amd64__) 31 | #include 32 | #define _pext_u32 vg_pext_u64 33 | #define _pext_u64 vg_pext_u64 34 | #define _pdep_u32 vg_pdep_u64 35 | #define _pdep_u64 vg_pdep_u64 36 | #endif 37 | #else 38 | #define vg_loadLUT64(p, i) p[i] 39 | #endif 40 | 41 | #define BCALL(N, X) N(r_uB(X)) 42 | #define interp_f64(X) r_u64f(X) 43 | 44 | #define SINGELI_FILE0(X) #X 45 | #define SINGELI_FILE1(X) SINGELI_FILE0(X) 46 | #define si_unreachable() UD 47 | #endif 48 | 49 | 50 | #pragma GCC diagnostic push 51 | #pragma GCC diagnostic ignored "-Wunused-variable" 52 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 53 | #pragma GCC diagnostic ignored "-Wunused-label" 54 | #include SINGELI_FILE1(../../build/obj2/SINGELI_DIR/SINGELI_FILE.c) 55 | #pragma GCC diagnostic pop 56 | 57 | #undef SINGELI_FILE 58 | -------------------------------------------------------------------------------- /test/cases/fuzz/bit.bqn: -------------------------------------------------------------------------------- 1 | ### WHOLE-FILE-TEST 2 | # •bit tests 3 | helpers ← ⊑•args 4 | r ← (helpers.GetRand@) 5 | ⟨RandVals, casts⟩ ← ⟨r⟩ helpers.Import "utils.bqn" 6 | widths ← ⊑¨casts 7 | 8 | # Test •bit._cast first 9 | # Convert unsigned width 𝕗 to 𝕘 10 | _cvtu_ ← { 11 | 𝕗=𝕘 ? 𝕩 ; 12 | 𝕗<𝕘 ? b←2⋆𝕗 ⋄ +⟜(b⊸×)˝⍉∘‿(𝕘÷𝕗)⥊𝕩 ; 13 | b←2⋆𝕘 ⋄ ⥊⍉>1(-⊸↓-b×↓)⌊∘÷⟜b⍟(↕1+𝕗÷𝕘)𝕩 14 | } 15 | _bitcast ← { fr‿to _𝕣 𝕩: 16 | S ← {𝕊:𝕩+𝕨×𝕩<0; 𝕊⁼:𝕩-𝕨×𝕩≥𝕨÷2} 17 | _off ← {(⊑"uci"⊐1⊑𝕗)⊑⟨⊢,-⟜@,(2⋆⊑𝕗)⊸S⟩} 18 | f‿t ← ⊑¨𝕗 19 | to _off⁼ f _cvtu_ t fr _off 𝕩 20 | } 21 | 22 | TestCast ← { f‿t 𝕊 len: 23 | c ← 𝕨 ⊏ casts 24 | len +↩ (1⌈÷˜´⊑¨c) | -len # Round up to exact conversion 25 | (c _bitcast ≡ c •bit._cast) len RandVals f 26 | } 27 | cs ← / 64>widths # TODO floats 28 | cx ← ⊏⟜(32‿'c'⊸≢¨casts)⊸/ cs # TODO 32-bit char output 29 | ls ← (↕100) ∾ r.Range¨⊸+ ⌊ 100 × ↕∘⌈⌾(1.5⊸⋆⁼) 100 30 | {cs (⋈ !∘TestCast 𝕩˙)⌜ cx}¨ ls 31 | 32 | # Now other •bit operations 33 | OpArgs ← {_b‿F‿a: 34 | m←(10} 45 | assert{(csz>1) & (csz<8)} 46 | def bulk = arch_defvw{} / 8 47 | def V = [bulk]u8 48 | bitalign{tup{2,8,csz}, 8, {s, align} => { 49 | @for_masked{bulk}(dst in tup{V,*u8~~dst} over cam) { 50 | dst = align{load{*V~~src}} 51 | ptr_add{u8, src, bulk*s/8} 52 | } 53 | }} 54 | } 55 | export{'si_bitwiden_n_8', bitwiden_n_8} 56 | }) 57 | 58 | (if (hasarch{'AARCH64'}) { 59 | fn bitnarrow_8_n(src:*void, dst:*void, csz:ux, cam:ux) : void = { 60 | assert{cam>0} 61 | assert{(csz>1) & (csz<8)} 62 | def bulk = arch_defvw{} / 8 63 | def V = [bulk]u8 64 | dstC:= *u8~~dst 65 | dstE:= *u8~~dst + cdiv{csz*cam, 8} 66 | bitalign{8, tup{2,8,csz}, {s, align} => { 67 | def get{} = align{load{*V~~src}} 68 | def next{} = { 69 | ptr_add{u8, src, bulk} 70 | ptr_add{u8, dstC, bulk*s/8} 71 | } 72 | while (dstC+bulk < dstE) { 73 | store{*V~~dstC, 0, get{}} 74 | next{} 75 | } 76 | while (dstC < dstE) { 77 | store_narrow{dstC, 0, get{}, mask_first{dstE - dstC}} 78 | next{} 79 | } 80 | }} 81 | } 82 | export{'si_bitnarrow_8_n', bitnarrow_8_n} 83 | }) -------------------------------------------------------------------------------- /test/cases/perf.bqn: -------------------------------------------------------------------------------- 1 | %DEF var V←•internal.Variation ⋄ LV←•internal.ListVariations ⋄ CLR←•internal.ClearRefs 2 | %DEF tvar %USE var ⋄ _tvar ← {F _𝕣 x: (CLR@) ⊢ {F 𝕩 V x}¨ LV 𝕩; w F _𝕣 x: (CLR@) ⊢ (LV 𝕨) {(𝕨 V w) F 𝕩 V x}⌜ LV 𝕩} 3 | %DEF fastone ( 4 | _fastone ← { F _𝕣 𝕩: 5 | t ← 0 6 | n ← 𝕩 7 | @∘F¨ ↕n 8 | { 𝕊: 9 | c ← F•_timed @ 10 | "wasn't fast enough" ! (n<10) ∨ t<5 11 | t+↩ c 12 | n+↩ 1 13 | c≥10000e¯9 14 | }•_while_⊢ 1 15 | n 16 | } 17 | ) 18 | 19 | # in-place ⌾(n⊸⊑) 20 | ( 21 | %USE fastone 22 | { 𝕊 to‿ti: 23 | a←b← to•internal.Variation 10⥊ < ti•internal.Variation ↕1e6 24 | n ← {𝕊: a 10⊸+⌾(4⊸⊑)⌾(1⊸⊑) ↩} _fastone 10 25 | ! (4+10×n) ≡ 4⊑1⊑a 26 | }¨ ⟨"Ah"‿"Ai32", "Af"‿"Ai32", "Sh"‿"Ai32", "Sf"‿"Ai32", "Ah"‿"Ah"⟩ 27 | ) 28 | 29 | # in-place ⌾⊑ 30 | %USE fastone ⋄ a←5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾⊑↩}_fastone 4 ⋄ ! (10↑a) ≡ (5+n)∾6+↕9 31 | 32 | # in-place ⌾(l⊸⊏) list 33 | %USE fastone ⋄ a←⋈¨5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! (⋈¨5‿6‿7‿8‿9‿1000000‿1000001‿1000002‿1000003‿1000004+0‿n‿0‿n‿0‿0‿0‿0‿0‿n) ≡ (5↑a)∾¯5↑a %!DEBUG 34 | %USE fastone ⋄ a←5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! ( 5‿6‿7‿8‿9‿1000000‿1000001‿1000002‿1000003‿1000004+0‿n‿0‿n‿0‿0‿0‿0‿0‿n) ≡ (5↑a)∾¯5↑a %!DEBUG 35 | %USE fastone ⋄ a←5+1e6⥊@+↕10000 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! (@+5‿6‿7‿8‿9‿10000‿10001‿10002‿10003‿10004 +0‿n‿0‿n‿0‿0‿0‿0‿0‿n) ≡ (5↑a)∾¯5↑a %!DEBUG 36 | %USE fastone ⋄ a←5+1e6⥊@+↕10000 ⋄ n←{𝕊: a'?'¨⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! (@+5‿6‿7‿8‿9‿10000‿10001‿10002‿10003‿10004{𝕩?63; 𝕨}¨0‿1‿0‿1‿0‿0‿0‿0‿0‿1) ≡ (5↑a)∾¯5↑a %!DEBUG 37 | %USE fastone ⋄ a←5+1e6⥊@+↕10000 ⋄ n←{𝕊: a 99¨⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! ( 5‿6‿7‿8‿9‿10000‿10001‿10002‿10003‿10004{𝕩?99;@+𝕨}¨0‿1‿0‿1‿0‿0‿0‿0‿0‿1) ≡ (5↑a)∾¯5↑a %!DEBUG 38 | 39 | # in-place ⌾(l⊸⊏) highrank 40 | %USE fastone ⋄ a←∘‿2‿2⥊⋈¨5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! a ≡ (n×⟨1,3,¯1+≠a⟩∊˜↕≠a) + ∘‿2‿2⥊⋈¨5+↕1e6 %!DEBUG 41 | %USE fastone ⋄ a←∘‿2‿2⥊5+↕1e6 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! a ≡ (n×⟨1,3,¯1+≠a⟩∊˜↕≠a) + ∘‿2‿2⥊5+↕1e6 %!DEBUG 42 | %USE fastone ⋄ a←∘‿2‿2⥊5+1e6⥊@+↕10000 ⋄ n←{𝕊: a 1⊸+⌾(1‿3‿3‿¯1⊸⊏)↩}_fastone 4 ⋄ ! a ≡ (n×⟨1,3,¯1+≠a⟩∊˜↕≠a) + ∘‿2‿2⥊5+1e6⥊@+↕10000 %!DEBUG 43 | 44 | # in-place ∾⟜atom & ∾⟜list 45 | %USE fastone ⋄ %USE tvar ⋄ LV↩"f"⊸LV ⋄ {𝕊v: j←<⍟(×≡) v ⋄ {a←𝕩 ⋄ n←{𝕊: a∾↩j}_fastone 4 ⋄ !( n +≠𝕩)≡≠a ⋄ !a≡v¨a}_tvar 1e6 ⥊<𝕩}¨ ⟨1, 1‿2, 'a'⟩ 46 | %USE fastone ⋄ %USE tvar ⋄ LV↩"f"⊸LV ⋄ {𝕊v: j←10⥊ 0; {(i8)} => 1; {(i16)} => 2; {(i32)} => 3; {(f64)} => 4 27 | {(u8)} => 5; {(u16)} => 6; {(u32)} => 7 28 | } 29 | 30 | def cbqn_tyArrOffset{} = emit{u64, 'offsetof', 'TyArr', 'a'} 31 | 32 | def talloc{T, len} = emit{*T, 'TALLOCP', fmt_type{T}, len} 33 | def tfree{ptr} = emit{void, 'TFREE', ptr} 34 | def fmt_type{T} = merge{quality{T}, fmtnat{width{T}}} 35 | def fmt_type{*T} = merge{'*',fmt_type{T}} 36 | 37 | def el_bit = 0 38 | def el_i8 = 1 39 | def el_i16 = 2 40 | def el_i32 = 3 41 | def el_f64 = 4 42 | def el_c8 = 5 43 | def el_c16 = 6 44 | def el_c32 = 7 45 | def el_B = 8 46 | 47 | def get_range{elt, data:*_, len:ux if any_int{elt} and (not knum{elt} or (elt>=el_bit and elt<=el_f64))} = { 48 | def fns = emit{*fntype{*void, *i64, u64, u1}, '', 'getRange_fns'} 49 | buf:*i64 = undefined{i64, 2} 50 | def ok = load{fns, elt}(*void~~data, buf, len) 51 | tup{ok, load{buf,0}, load{buf,1}} 52 | } 53 | 54 | def get_range{data:*E, len} = get_range{match(E) { 55 | {(u1)} => el_bit 56 | {(i8)} => el_i8 57 | {(i16)} => el_i16 58 | {(i32)} => el_i32 59 | {(f64)} => el_f64 60 | }, data, len} 61 | def get_range{data:*E, s, e} = get_range{data+s, e-s} 62 | 63 | 64 | local def root{G, T} = G{T} 65 | local def root{G, [_]E} = G{E} 66 | 67 | # total comparisons 68 | def tot_gt{a:T, b:T if root{isfloat,T}} = (b==b) &~ (a<=b) 69 | def tot_ge{a:T, b:T if root{isfloat,T}} = (a!=a) | (a>=b) 70 | 71 | def tot_gt{a:V=[_]E, b:V if isfloat{E} and hasarch{'X86_64'} and width{V}<=256} = ord{V~~(a<=b), b} 72 | def tot_ge{a:V=[_]E, b:V if isfloat{E} and hasarch{'X86_64'} and width{V}<=256} = unord{V~~(a>=b), a} 73 | 74 | def tot_gt{a:T, b:T if root{isint,T}} = a > b 75 | def tot_ge{a:T, b:T if root{isint,T}} = a >= b 76 | 77 | def tot_lt{a, b} = tot_gt{b, a} 78 | def tot_le{a, b} = tot_ge{b, a} 79 | -------------------------------------------------------------------------------- /docs/commands.md: -------------------------------------------------------------------------------- 1 | # CBQN system commands 2 | 3 | These are commands usable from a CBQN REPL that, for one reason or another, aren't suited to be system functions. 4 | 5 | ## `)exit` / `)off` 6 | 7 | Equivalent to `•Exit 0` 8 | 9 | ## `)ex path/to/file` 10 | 11 | Execute the contents of the file as if it were REPL input (but allowing multiline definitions). Not a system function because modifying the list of global variables during execution is not allowed. 12 | 13 | ## `)r expr` 14 | 15 | Execute the expression, but don't print its result. 16 | 17 | ## `)clearImportCache` 18 | 19 | Clear the cache of monadic `•Import` calls, resulting in re-evaluating the source the next time one is executed. 20 | 21 | ## `)t expr` / `)time expr` / `)t:n expr` / `)time:n expr` 22 | 23 | Time the argument expression. `n` specifies the number of times to repeat. Exists to allow not escaping quotes and less overhead for timing very fast & small expressions. 24 | 25 | ## `)explain expr` / `)e expr` 26 | 27 | Display a syntax breakdown of the expression 28 | 29 | ## `)profile expr` / `)profile@frequency expr` 30 | 31 | Profile the expression at the given sampling frequency, or 5000 samples/second by default. 32 | 33 | ## `)vars` 34 | 35 | List the globally defined variables. 36 | 37 | ## `)theme name` (replxx-only) 38 | 39 | Changes the color scheme of syntax highlighting; Supported: `)theme light`, `)theme dark`, `)theme none`. 40 | 41 | ## `)kb` (replxx-only) 42 | 43 | Enable or disable backslash input for BQN characters. 44 | 45 | ## `)kb character` (replxx-only) 46 | 47 | Set keyboard prefix character to a custom one. Default is backslash, i.e. `)kb \` 48 | 49 | ## `)erase name` 50 | 51 | Erase the specified variable name. 52 | 53 | Not a system function because it only clears the variables value (previous code `↩`ing it will still be able to), and to allow it to be executed even when the VM is completely out of memory such that it can't even parse REPL input BQN. 54 | 55 | ## `)escaped "escaped expr"` 56 | 57 | Parse `\`-escapes and execute as REPL input. Primarily for external tools to be able to execute multiline input. 58 | 59 | ## `)mem` 60 | 61 | Get statistics on memory usage. 62 | 63 | `)mem t` to get usage per object type. 64 | `)mem s` to get a breakdown of the number of objects with a specific size. 65 | `)mem f` to get breakdown of free bucket counts per size. 66 | `)mem log` enables/disabled printing a message on OS requests for more memory. 67 | 68 | ## `)gc` 69 | 70 | Force garbage collection. 71 | 72 | `)gc disable` disables automatic garabage collection, and `)gc enable` enables it again. 73 | 74 | A plain `)gc` differs from `•internal.GC@` in that it can do precise marking as there's no in-progress C stack. 75 | 76 | `)gc log` enables/disables printing a message on GC. 77 | 78 | 79 | ## `)internalPrint expr` 80 | 81 | Use the internal object printing system to show the expression result. Mainly for debugging in situations where the BQN self-hosted formatter can't be used. -------------------------------------------------------------------------------- /src/singeli/src/selfsearch.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | local include 'skin/cext' 3 | include './hashtab' 4 | 5 | # Resizing hash table, with fallback 6 | def rty{name} = if (to_prim{name}=='∊') i8 else i32 7 | fn selfhashtab{T, name}(rpi:*rty{name}, xp:*T, n:usz) = { 8 | def wt = width{T} 9 | def prim = to_prim{name} 10 | def has_radix = if (prim=='⊐') 0 else wt==32 11 | n64 := promote{u64,n} 12 | rp := if (prim=='∊') rpi else *u32~~rpi 13 | def {res0,cc_stop} = if (prim=='∊') tup{1,n64>>has_radix} else tup{0,2*n64} 14 | 15 | log := clzc{n} 16 | # Max size 17 | msl := clzc{n+n/2} + 1; if (has_radix and msl>20) msl=20 18 | msz := usz~~1 << msl 19 | # Starting log-size 20 | sl := msl; if (msl>=14) sl = 12+(msl&1) 21 | b:usz = 64 # Block size 22 | 23 | # Filling e slots past the end requires e*(e+1)/2 collisions, so 24 | # n entries with <2 each can fill 0} 27 | rp <-{0} res0 28 | def aux = prim!='∊' 29 | def {{hash,...vals}, sz, sh, div_thresh, hash_resize, hash_free} = hash_alloc{ 30 | sl, msz, ext, tup{T, ...aux**u32}, tup{x0, ...aux**0}, has_radix, 0 31 | } 32 | 33 | def {output, write_res} = match (prim) { 34 | {'∊'} => tup{{b}=>b, {j,h,k,x0} => { hash<-{j}h; promote{i8,k!=h} }} 35 | {'⊒'} => { 36 | ctr0:u32 = 1; def {val} = vals 37 | def res{j,h,k,x0} = { 38 | e0:=promote{u32,h==x0}; vj:=val->j; c0:=ctr0 39 | hash<-{j}h; val<-{j}vj+(e0^1); ctr0+=e0 40 | vj + (c0 & -e0) 41 | } 42 | tup{{b}=>b, res} 43 | } 44 | {'⊐'} => { 45 | ctr:u32 = 1; def {val} = vals 46 | def res{j,h,k,x0} = { 47 | if (k!=h) { val<-{j}ctr; ++ctr; hash<-{j}h } 48 | val->j 49 | } 50 | tup{{b}=>ctr & -promote{u32,b}, res} 51 | } 52 | } 53 | 54 | def break=makelabel{} 55 | cc:u64 = 0 # Collision counter 56 | i:usz=1; while (1) { 57 | e := tern{n-i>b, i+b, n} 58 | while (i < e) { 59 | h := hash_val{xp->i}; j0 := h>>sh; j := j0 60 | k:=undefined{T}; while (((k=hash->j)!=h) & (k!=x0)) ++j 61 | cc += cast_i{u64, j-j0} 62 | rp <-{i} write_res{j,h,k,x0} 63 | ++i 64 | } 65 | if (i == n) goto{break} 66 | # Check collision counter and possibly resize 67 | def p64 = promote{u64,.} 68 | dc := cc - p64{div_thresh{i}} 69 | if (i64~~dc >= 0) { 70 | if (sz == msz) goto{break} # Abort 71 | if (has_radix and i < n/2 and sz >= 1<<18) goto{break} 72 | # Avoid resizing if close to the end 73 | if (cc>=cc_stop or p64{n-i}*dc >= (p64{i}*p64{n+i})>>(5+log-(wt-sh))) { 74 | hash_resize{cc, 2} # Factor of 4 75 | if (cc >= cc_stop) goto{break} 76 | } 77 | } 78 | } 79 | setlabel{break} 80 | hash_free{} 81 | output{i == n} # Whether it finished, and unique count if ⊐ 82 | } 83 | 84 | def exp_hash{T, name} = { 85 | export{merge{name,'_c1_hash',fmtnat{width{T}}}, selfhashtab{T, name}} 86 | } 87 | each{{n}=>each{exp_hash{.,n},tup{u32,u64}}, names} 88 | -------------------------------------------------------------------------------- /src/singeli/c/arithdDispatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if SINGELI_SIMD 3 | // #define ARITH_DEBUG 1 4 | 5 | #define FOR_ExecAA(F) \ 6 | F(fail) /* first to allow zero-initialization to be fail implicitly */ \ 7 | F(swap) /* swap 𝕨 and 𝕩, then run ex2 */ \ 8 | /* cast the specified argument up to the specified size, then either swap or don't, then run ex2 */ \ 9 | F(wi8_reg) F(xi8_reg) F(wi8_swap) F(xi8_swap) \ 10 | F(wi16_reg) F(xi16_reg) F(wi16_swap) F(xi16_swap) \ 11 | F(wi32_reg) F(xi32_reg) F(wi32_swap) F(xi32_swap) \ 12 | F(wf64_reg) F(xf64_reg) F(wf64_swap) F(xf64_swap) \ 13 | F(wc16_reg) F(xc16_reg) F(wc16_swap) F(xc16_swap) \ 14 | F(wc32_reg) F(xc32_reg) F(wc32_swap) F(xc32_swap) \ 15 | /* c_* - overflow-checked; u_* - no overflow check */ \ 16 | F(c_call_rbyte) /* arguments are already the wanted widths; result isn't a bitarr */ \ 17 | F(u_call_rbyte) /* ↑ */ \ 18 | F(e_call_rbyte) /* calls ChkFnAA but errors on non-zero result */ \ 19 | F(u_call_bit) /* result and arguments are bitarrs */ \ 20 | F(u_call_wxf64sq) /* convert both args up to f64 if needed, make f64arr, and squeeze result; i.e. lazy float fallback case */ \ 21 | F(c_call_wxi8) /* convert both args (which need to be bitarrs) to i8arrs, and invoke checked function (no good reason for it to fail the check, but this allows reusing a c‿i8‿i8 impl) */ \ 22 | F(e_call_sqx) /* squeeze f64arr 𝕩, error if can't; else re-dispatch to new entry */ 23 | 24 | enum ExecAA { 25 | #define F(X) X, 26 | FOR_ExecAA(F) 27 | #undef F 28 | }; 29 | 30 | #if ARITH_DEBUG 31 | char* execAA_repr(u8 ex); 32 | #endif 33 | 34 | typedef u64 (*ChkFnAA)(void* r, void* w, void* x, u64 len); 35 | typedef void (*UnchkFnAA)(void* r, void* w, void* x, u64 len); 36 | 37 | typedef struct FnInfoAA { 38 | union { ChkFnAA cFn; UnchkFnAA uFn; }; 39 | u8 ex1, ex2; // ExecAA 40 | u8 type; // t_*; unused for u_call_bit 41 | u8 width; // width in bytes; unused for u_call_bit 42 | } FnInfoAA; 43 | 44 | typedef struct EntAA { 45 | FnInfoAA bundles[2]; 46 | } EntAA; 47 | 48 | typedef struct DyTableAA { 49 | EntAA entsAA[el_B*el_B]; // one for each instruction 50 | FC2 mainFn; 51 | char* repr; 52 | } DyTableAA; 53 | 54 | 55 | 56 | 57 | 58 | typedef struct DyTableSA DyTableSA; 59 | typedef bool (*ForBitsel)(DyTableSA*, B w, B* r); 60 | typedef u64 (*ChkFnSA)(void* r, u64 w, void* x, u64 len); 61 | 62 | typedef struct { 63 | // >=el_i8 el_bit 64 | union { ChkFnSA f1; ForBitsel bitsel; }; 65 | union { ChkFnSA f2; }; 66 | } EntSA; 67 | 68 | 69 | struct DyTableSA { 70 | EntSA ents[el_B]; 71 | FC2 mainFn; 72 | char* repr; 73 | u8 fill[2][2]; // 0:none 1:int 2:char 74 | DyTableSA* chrAtom; 75 | }; 76 | 77 | // all assume f is a function, and return NULL if a table is unavailable (either because f isn't applicable dyadic arith, or the requested case always errors) 78 | DyTableAA* dyTableAAFor(B f); 79 | DyTableSA* dyTableSAFor(B f, bool atomChar); 80 | DyTableSA* dyTableASFor(B f, bool atomChar); // returns table taking arguments in reverse order 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /src/singeli/src/bit_arith.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | include './cbqnDefs' 3 | include './f64' 4 | include './bitops' 5 | include './mask' 6 | include './dyarith_common' 7 | 8 | def __mul{a:(u16), b:(u16)} = trunc{u16, promote{u32,a} * promote{u32,b}} 9 | 10 | if_inline (hasarch{'X86_64'}) { 11 | def __mul{a:V=[k](u8), b:V if hasarch{'SSSE3'}} = { 12 | def VI8 = [k]i8 13 | def m = make_cycle{VI8, -1,0} 14 | def lo = mul_sum_sat{2, a, VI8~~b & m} 15 | def hi = mul_sum_sat{2, a, VI8~~b &~ m} 16 | V~~((lo&re_el{i16,m}) | (hi<<8)) 17 | } 18 | def __mul{a:V=[k](u64), b:V if not hasarch{'AVX512DQ'}} = { 19 | def h = mul32{a, b>>32} + mul32{a>>32, b} 20 | mul32{a, b} + (h<<32) 21 | } 22 | } 23 | 24 | 25 | 26 | 27 | 28 | fn arith_aa{'s', F, T, T, T}(r:*void, w:*void, x:*void, len:u64) : void = { 29 | assert{len != 0} 30 | @for (r in *T~~r, w in *T~~w, x in *T~~x over len) r = F{w, x} 31 | } 32 | fn arith_sa{'s', F, swap, T, T, T}(r:*void, w:u64, x:*void, len:u64) : void = { 33 | assert{len != 0} 34 | def w = get_packed_scal{T, w} 35 | @for (r in *T~~r, x in *T~~x over len) { 36 | r = (if (swap) F{x, w} else F{w, x}) 37 | } 38 | } 39 | 40 | 41 | 42 | fn bit_dy_as{F}(r: *u8, w: *u8, x_splat:u64, len:u64) : void = { 43 | if (has_simd) { 44 | def vw = arch_defvw{} 45 | assert{vw>=64} 46 | def bulk = vw / width{u8} 47 | def T8 = [bulk]u8 48 | def T64 = [bulk/8]u64 49 | x:= T8~~T64**x_splat 50 | @for_mu{bulk, 2}(sr in tup{'g',r}, cw in tup{T8,w} over len) sr{eachx{F, cw, x}} 51 | } else { 52 | def val{o} = F{loadu{u64, w+o}, x_splat} 53 | @for (i to len/8) { 54 | def o = i*8 55 | storeu{u64, r+o, val{o}} 56 | } 57 | def o = len&~7 58 | if (o != len) { 59 | def b = loadu{u64, r+o} 60 | def m = tail{u64, (len&7)*8} 61 | storeu{u64, r+o, (val{o} & m) | (b &~ m)} 62 | } 63 | } 64 | } 65 | 66 | def bit_dy_aa{F} = arith_aa{if (has_simd) 0 else 's', F, u8, u8, u8} 67 | 68 | 69 | 70 | def arith_op{F, class} = { 71 | def ts = tup{u8, u16, u32, u64} 72 | 73 | def vec{..._} = has_simd 74 | def vec{(u64), ..._ if hasarch{'AARCH64'} and same{F,__mul}} = 0 75 | def vec{E, ..._ if same{F,__mul} and hasarch{'X86_64'} and ((E==u8 and not hasarch{'SSSE3'}) or (E==u32 and not hasarch{'AVX2'}) or (E==u64 and not hasarch{'AVX2'}))} = 0 76 | def vec{..._ if hasarch{'X86_64'} and class==1} = 0 77 | 78 | def sa_impl{swap}{T} = arith_sa{if (vec{T,swap}) 4 else 's', F, swap, T, T, T} 79 | def aa_impl{T} = arith_aa{if (vec{T}) 0 else 's', F, T, T, T} 80 | 81 | def me{'sa'} = each{sa_impl{0}, ts} 82 | def me{'as'} = each{sa_impl{1}, ts} 83 | def me{'aa'} = each{aa_impl, ts} 84 | } 85 | 86 | def bit_ops = tup{&, |, ^} 87 | 88 | def arith_ops = tup{ 89 | arith_op{+, 0}, 90 | arith_op{-, 0}, 91 | arith_op{*, 0}, 92 | } 93 | 94 | export_tab{'si_bitwise_aa', each{bit_dy_aa, bit_ops}} 95 | export_tab{'si_bitwise_as', each{bit_dy_as, bit_ops}} 96 | 97 | export_tab{'si_bitarith_aa', join{each{{c} => c{'aa'}, arith_ops}}} 98 | export_tab{'si_bitarith_sa', join{each{{c} => c{'sa'}, arith_ops}}} 99 | 100 | export{'si_andBytes', bit_dy_as{&}} # for andBytes_fn 101 | -------------------------------------------------------------------------------- /src/singeli/src/avx512.singeli: -------------------------------------------------------------------------------- 1 | local { 2 | def has512 = x86_has512 3 | def has512e = x86_has512e 4 | def intrin = x86_intrin 5 | def intrin_t = x86_intrin_t 6 | def intrin_i = x86_intrin_i 7 | def mt{k} = merge{'mask', fmtnat{__max{k,8}}} 8 | def mr{s} = merge{s, '_mask'} 9 | } 10 | 11 | def store_masked_hom{p:*E, m:[l](u1), v:V=[l]E if has512e{V}} = { 12 | emit{void, intrin_i{V, 'mask_storeu'}, p, m, v} 13 | } 14 | 15 | 16 | 17 | # mask conversions 18 | local def mask_int_ty{k} = ty_u{__max{k,8}} 19 | local def mask_int_op{k, s} = if (hasarch{if (k<=8) 'AVX512DQ' else if (k==16) 'AVX512F' else 'AVX512BW'}) s else '' 20 | def hom_to_int{x:[k](u1)} = emit{mask_int_ty{k}, mask_int_op{k, merge{'_cvt',mt{k},'_u',fmtnat{__max{k,32}}}}, x} 21 | def int_to_mask{[k](u1), x:T if isunsigned{T} and width{T}>=k} = emit{[k]u1, mask_int_op{k, merge{'_cvtu',fmtnat{__max{k,32}},'_',mt{k}}}, x} 22 | def int_to_mask{[k](u1), x if int_idx{x, 1<=32) 'AVX512DQ' else 'AVX512BW'}} = emit{V, intrin_i{V,'movm'}, x} 38 | def hom_to_hom{V=[k]E, x:[k](u1) if E!=u1} = mask_to_hom{V,x} 39 | 40 | def lowelt_mask{[k](u1), n if any_int{n}} = int_to_mask{[k]u1, bzhi{hom_ones{u64}, n}} 41 | 42 | def sel{(ty_u{V}), x:V=[_]E, i:I==(ty_u{V}) if (if (width{E}>8) has512e{V} else has512{V, 'VBMI'})} = emit{V, intrin_i{V, 'permutexvar'}, i, x} 43 | 44 | def multishift{a:[k](u64), i:V=[(k*8)](u8) if has512{V, 'VBMI'}} = emit{V, intrin_i{V, 'multishift_epi64'}, i, a} 45 | 46 | def narrow{DE, x:[k]SE if isint{DE} and quality{DE}==quality{SE} and x86_has512e{[k]SE}} = { 47 | emit{x86_vec_low{k,DE}, intrin{[k]SE, 'cvtepi', fmtwidth{SE}, '_epi', fmtwidth{DE}}, x} 48 | } 49 | 50 | local def masked_op{name, pattern} = { 51 | def ok{args, k, base} = { 52 | def got = match (...args) { 53 | (pattern) 54 | {..._} => tup{} 55 | } 56 | match (got, k, base) { 57 | {{V=[k]_, ..._}, k, 0 } => 1 58 | {{V=[k]_, ..._}, k, _:V} => 1 59 | {..._} => 0 60 | } 61 | } 62 | def me{...args, m:[k](u1), base if ok{args, k, base}} = { 63 | def {V,...rest} = pattern{...args} 64 | def has_base = not is{base,0} 65 | emit{V, 66 | intrin_i{V, merge{if (has_base) 'mask_' else 'maskz_', name}}, 67 | ...has_base**base, 68 | m, 69 | ...rest 70 | } 71 | } 72 | } 73 | 74 | def masked_op1{name} = masked_op{name, {a:V if has512e{V}} => tup{V,a}} 75 | def masked_mov{...} = masked_op1{'mov'} 76 | def compress{...} = masked_op1{'compress'} 77 | -------------------------------------------------------------------------------- /src/singeli/src/scan_common.singeli: -------------------------------------------------------------------------------- 1 | # Used by scan.singeli and bins.singeli 2 | 3 | def has_sel8 = hasarch{'SSSE3'} or hasarch{'AARCH64'} 4 | def sel8{v:V, t} = sel{[16]u8, v, make{re_el{i8,V}, t}} 5 | def sel8{v:V, t if w256{V} and ktup{t} and length{t}==16} = sel8{v, merge{t,t}} 6 | 7 | local def rev{t} = { def l=length{t}; def j=l-1; select{j-t, j-range{l}} } 8 | local def rev{up,t} = if (up) t else rev{t} 9 | def sel8{v, t, up} = sel8{v, rev{up,t}} 10 | 11 | def zip{up, x if knum{up}} = zip{x, x, up} 12 | 13 | # Fill last 4 bytes with last element, in each lane 14 | def spread{a:[_]T, ...up} = { 15 | def w = width{T} 16 | def b = w/8 17 | if (w<=16) sel8{a,merge{iota{12},(16-b)+iota{4}%b}, ...up} else a 18 | } 19 | 20 | # Set all elements with the last element of the input 21 | def broadcast_last{n:VT, up if has_simd and w128{VT}} = { 22 | def l{v, w} = l{zip{up,v}, 2*w} 23 | def l{v, w if has_sel8} = sel8{v, up*(16-w/8)+iota{16}%(w/8)} 24 | def l{v, w==32} = shuf{[4]i32, v, 4**(up*3)} 25 | def l{v, w==64} = shuf{[2]i64, v, 2** up } 26 | l{n, elwidth{VT}} 27 | } 28 | def broadcast_last{n:VT, up if hasarch{'AVX2'} and w256{VT}} = { 29 | if (elwidth{VT}<=32) sel{[8]i32, spread{n,up}, [8]i32**(up*7)} 30 | else shuf{[4]u64, n, 4**(up*3)} 31 | } 32 | def broadcast_last{n:[k]_, up if hasarch{'AARCH64'}} = broadcast_sel{n, if (up) k-1 else 0} 33 | def broadcast_last{n:VT} = broadcast_last{n, 1} 34 | 35 | # Make prefix scan from op and shifter by applying the operation 36 | # at increasing power-of-two shifts 37 | def prefix_byshift{op, sh} = { 38 | def pre{v:V, k} = if (k < width{V}) pre{op{v, sh{v,k}}, 2*k} else v 39 | {v:T} => pre{v, if (vect{T}) elwidth{T} else 1} 40 | } 41 | 42 | def get_id{op,T} = (match (op) { {(__min)}=>maxvalue; {(__max)}=>minvalue }){T} 43 | 44 | def make_scan_idem{T, op, up} = { 45 | # Within each lane, scan using shifts by powers of 2. First k elements 46 | # when shifting by k don't need to change, so leave them alone. 47 | def shift{k,l} = rev{up, merge{iota{k},iota{l-k}}} 48 | def shb{v:V, k} = { 49 | def w=width{T}; def c = k/w 50 | def merger{a,b} = if (up) merge{a,b} else merge{b,a} 51 | def id = make{V, merger{c**get_id{op,T}, (width{V}/w-c)**0}} 52 | (if (up) vec_shift_right_128 else vec_shift_left_128){v, c} | id 53 | } 54 | def shb{v, k if has_sel8} = sel8{v, shift{k/8,16}} 55 | def shb{v, k if k>=32} = shuf{[4]u32, v, shift{k/32,4}} 56 | def shb{v, k if k==128 and hasarch{'AVX2'}} = { 57 | # After lanewise scan, broadcast end of lane 0 to entire lane 1 58 | shuf{[8]i32, spread{v,up}, rev{up,3*(3refc!=0) { 10 | #if OBJ_COUNTER 11 | printf("delta %d for %s @ %p, uid "N64d": ", v->refc, type_repr(v->type), v, v->uid); 12 | #else 13 | printf("delta %d for %s @ %p: ", (i32)v->refc, type_repr(v->type), v); 14 | #endif 15 | heap_observed = v; 16 | printI(tag(v,OBJ_TAG)); printf("\n"); 17 | } 18 | } 19 | 20 | 21 | 22 | void gc_visitRoots(void); 23 | void gcv2_runHeapverify(i32); 24 | void cbqn_heapVerify() { 25 | heap_observed = 0; 26 | gcv2_runHeapverify(0); 27 | mm_forHeap(heapVerify_checkFn); 28 | gcv2_runHeapverify(1); 29 | if (heap_observed) printf("refc of last: %d\n", heap_observed->refc); 30 | heapVerify_mode=-1; 31 | } 32 | 33 | #endif 34 | 35 | STATIC_GLOBAL u64 heap_PICounts[t_COUNT]; 36 | STATIC_GLOBAL u64 heap_PISizes[t_COUNT]; 37 | 38 | NOINLINE void heap_PIFn(Value* v) { 39 | heap_PICounts[PTY(v)]++; 40 | heap_PISizes[PTY(v)]+= mm_size(v); 41 | } 42 | 43 | STATIC_GLOBAL u64 heap_PIFreed[128]; 44 | void heap_PIFreedFn(Value* v) { 45 | heap_PIFreed[v->mmInfo&127]++; 46 | } 47 | 48 | void mm_forFreedHeap(V2v f); 49 | void heap_printInfo(bool sizes, bool types, bool freed, bool chain) { 50 | u64 total = mm_heapAlloc; 51 | u64 used = tot_heapUsed(); 52 | fprintf(stderr, "RAM allocated: "N64u"\n", total); 53 | fprintf(stderr, "heap in use: "N64u"\n", used); 54 | #if MM!=0 55 | if (sizes) { 56 | for (i32 i = 2; i < 64; i++) { 57 | for (i32 j = 0; j < MM; j++) { 58 | i32 o = i-j; 59 | u64 count = mm_ctrs[o + j*64]; 60 | if (count>0) fprintf(stderr, "size %llu: "N64u"\n", (1ULL<0) fprintf(stderr, "type %d/%s: count "N64u", total size "N64u"\n", i, type_repr(i), count, heap_PISizes[i]); 70 | } 71 | } 72 | if (freed || chain) { 73 | if (freed) { 74 | for (i32 i = 0; i < MM*64; i++) heap_PIFreed[i] = 0; 75 | mm_forFreedHeap(heap_PIFreedFn); 76 | } 77 | 78 | for (i32 i = 2; i < 64; i++) { 79 | for (i32 j = 0; j < MM; j++) { 80 | i32 o = i-j; 81 | u64 cf=0, cc=0; 82 | if (freed) { 83 | cf = heap_PIFreed[o + j*64]; 84 | } 85 | 86 | if (chain) { 87 | i32 b = j*64 + i; 88 | if (mm_buckets[b]) { 89 | EmptyValue* p = mm_buckets[b]; 90 | while (p) { cc++; p = p->next; } 91 | } 92 | } 93 | if (cf!=0 || cc!=0) { 94 | if (chain && freed) fprintf(stderr, "freed size %llu: count "N64u", chain "N64u"\n", (1ULL<=2); 113 | } 114 | -------------------------------------------------------------------------------- /src/singeli/src/bitops.singeli: -------------------------------------------------------------------------------- 1 | def load_bit{x:(*u64), n:(ux)} = { 2 | ((load{x,n>>6}>>(n&63)) & 1) != 0 3 | } 4 | 5 | def load_bits_lo{sz, x:(*u64), n:(ux)} = match (sz) { 6 | {2} => (load{*u8~~x, n>>2} >> cast_i{u8, (n&3)*2}) 7 | {4} => (load{*u8~~x, n>>1} >> cast_i{u8, (n&1)*4}) 8 | {sz if sz>=8} => load{*ty_u{sz}~~x, n} 9 | } 10 | 11 | def load_bits{sz, x:(*u64), n:(ux)} = match (sz) { 12 | {2} => load_bits_lo{sz, x, n} & 3 13 | {4} => load_bits_lo{sz, x, n} & 15 14 | {sz if sz>=8} => load{*ty_u{sz}~~x, n} 15 | } 16 | 17 | 18 | def store_bit{x:(*u64), n:(ux), v:(u1)} = { 19 | m:u64 = cast{u64,1}<<(n&63) 20 | p:u64 = load{x,n>>6} 21 | if (v) store{x,n>>6,p | m} 22 | else store{x,n>>6,p & ~m} 23 | } 24 | 25 | def store_bits{sz, x:(*u64), n:(ux), v} = match (sz) { 26 | {4} => { 27 | x8:= *u8 ~~ x 28 | 29 | #w:u64 = cast_i{u64, load{x8,n/2}} 30 | #sh:u64 = (n&1) * 4 31 | #w&= ~(15< store{*u8 ~~ x, n, cast_i{u8, v}} 46 | {16} => store{*u16 ~~ x, n, cast_i{u16,v}} 47 | {32} => store{*u32 ~~ x, n, cast_i{u32,v}} 48 | {64} => store{ x, n, cast_i{u64,v}} 49 | {sz} => { 50 | vc:u64 = promote{u64,v} 51 | am:u64 = 64/sz 52 | w:u64 = load{x,n/am} 53 | sh:u64 = (n&(am-1)) * sz 54 | w&= ~(tail{u64,sz} << sh) 55 | w|= (vc<>3 + bit{4, idxs}}} 65 | e:= make{[32]u8, 1<=8}} 71 | andnz{b, make{T, 1<<(iota{16}&7)}} 72 | } 73 | def expand_bits{T==[16]u8, a:(u16) if hasarch{'X86_64'}} = { 74 | b:= T~~[8]u16**a 75 | exp:= T~~shuf{[4]i32, vec_shuffle16_lo{mzip{b,b,0}, tup{0,0,1,1}}, 0,0,1,1} 76 | (exp & make{T, 1<<(iota{16}&7)}) != T**0 77 | } 78 | 79 | def expand_bits{V=[k]T, a:A if k<=width{T} and quality{T}=='u'} = { 80 | b:= make{V, 1<>3)} >> (i&7) 92 | } 93 | def loadu_bits{x:(*u64), i, n} = { 94 | assert{n<58 or ((n==58 or n==60) and (i%n == 0))} 95 | loadu_bits_raw{x, i} 96 | } 97 | def loadu_bits_trunc{x:(*u64), i, n if knum{n}} = trunc_bits{n, loadu_bits{x, i, n}} 98 | 99 | 100 | def load_expand_bits{T, x:(*u64), {...is}} = { 101 | # def len = length{is} 102 | # def [count]_ = T 103 | # assert{count*len <= 64} 104 | # bits:= load_bits_lo{count*len, x, select{is,0}} 105 | # @collect(i to len) expand_bits{T, trunc_bits{count, bits>>(i*count)}} 106 | each{load_expand_bits{T, x, .}, is} 107 | } 108 | -------------------------------------------------------------------------------- /src/singeli/src/copy.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | include './mask' 3 | include './cbqnDefs' 4 | include './bitops' 5 | 6 | def copyFromBits{V=[bulk]T, loadFn, rp, l:(u64)} = { 7 | def U = ty_u{V} 8 | 9 | @for_masked{bulk}(sr in tup{'g',*T~~rp} over i to l) { 10 | x:= loadFn{U, i} 11 | sr{V~~(x & U ~~ V**1)} 12 | } 13 | } 14 | 15 | 16 | fn copy{X, R}(r: *void, x: *void, l:u64, xRaw: *void) : void = { 17 | def vw = arch_defvw{} 18 | assert{l!=0} 19 | 20 | def bulk = vw/__max{width{X}, width{R}} 21 | xp:= *tern{X==u1, u64, X} ~~ x 22 | rp:= *tern{R==u1, u64, R} ~~ r 23 | def XV = [bulk]X 24 | def RV = [bulk]R 25 | def ur = tern{hasarch{'AARCH64'}, 4, 2} 26 | 27 | if (X==R and R!=u1) { 28 | if (hasarch{'X86_64'} and l<=bulk) store_narrow{rp, 0, load_widen{xp, 0, RV}, mask_first{l}} 29 | else emit{void, 'memcpy', r, x, l*(width{X}/8)} 30 | } else if (R==u64) { 31 | # show{'R==u64', X, R} 32 | assert{X==u8 or X==u16 or X==u32} 33 | # TODO could maybe read 256 bits and use unpack to write >256 34 | @for_mu{bulk,ur}(sr in tup{'g',rp}, x in tup{RV,xp} over l) sr{eachx{|, x, RV**(cbqn_c32Tag{}<<48)}} 35 | } else if (X==u1 and R==u1) { 36 | # show{'u1u1', X, R} 37 | def V64 = [vw/64]u64 38 | @for_mu{vcount{V64},ur}(sr in tup{'g',rp}, x in tup{V64,xp} over cdiv{l,64}) sr{x} 39 | } else if (X==u1) { 40 | # show{'X==u1', X, R} 41 | copyFromBits{[bulk]R, load_expand_bits{., xp, .}, r, l} 42 | } else if (R==u1) { 43 | # show{'R==u1', X, R} 44 | 45 | def XU = ty_u{XV} 46 | def op{x} = (XU~~x) == XU~~XV**1 47 | 48 | def unr = if (hasarch{'X86_64'} and width{X}==16) 2 else __max{8/bulk, 1} 49 | def bulk2 = bulk*unr 50 | xi:ux = 0 51 | @for_nz (i to cdiv{l,bulk2}) { 52 | store_bits{bulk2, rp, i, hom_to_int{each{{i} => op{load_widen{xp, xi+i, XV}}, iota{unr}}}} 53 | xi+= unr 54 | } 55 | } else if (width{X}<=width{R}) { 56 | # show{'w{X}<=w{R}', X, R} 57 | @for_mu{bulk,ur}(sr in tup{'g',rp}, x in tup{RV,xp} over l) sr{x} 58 | } else { 59 | # show{'w{X}>w{R}', X, R} 60 | @for_mu{bulk,ur}(sr in tup{'g',rp}, x in tup{XV,xp} over l) sr{x} 61 | } 62 | } 63 | 64 | fn copy_ubit{R}(r: *void, x: *void, l:u64, xRaw: *void) : void = { 65 | assert{l!=0} 66 | x0:= (*u8~~xRaw) + cbqn_tyArrOffset{} 67 | xs:= u64~~((*u8~~x) - x0) 68 | # if ((xs&7)==0) { 69 | # copy{u1, R}(*void~~(x0 + (xs>>3)), r, l, xRaw) 70 | # } else { 71 | def vw = arch_defvw{} 72 | def bulk = vw/width{R} 73 | def RV = [bulk]R 74 | rp:= *R~~r 75 | 76 | copyFromBits{RV, {T=[k]_, i} => expand_bits{T, loadu_bits_trunc{*u64~~x0, xs+i*k, k}}, r, l} 77 | # } 78 | } 79 | 80 | 81 | def gen{p} = { 82 | def ts = tup{u1, i8, i16, i32, f64, u8, u16, u32, u64} 83 | def tn = tup{'1','i8','i16','i32','f64','c8','c16','c32','B'} 84 | def tm = tup{0, 0, 0, 0, 0, 1, 1, 1, 2} 85 | each{{tx0,nx,mx} => { 86 | each{{tr0,nr,mr} => { 87 | if ((mx==mr or mx==2 or mr==2) and (if (mx==2) mr==1 else 1)) { 88 | def tr = if (mx==0 and mr==2) f64 else if (tx0==tr0 and mx==1) ty_s{tx0} else tr0 89 | def tx = if (mr==0 and mx==2) f64 else if (tx0==tr0 and mx==1) ty_s{tx0} else tx0 90 | export{merge{p, nx, '_', nr}, copy{tx, tr}} 91 | } 92 | }, ts, tn, tm} 93 | }, ts, tn, tm} 94 | } 95 | 96 | gen{'simd_copy_'} 97 | 98 | # unaligned bitarr widening 99 | export{'simd_copy_1u_i8', copy_ubit{i8}} 100 | export{'simd_copy_1u_i16', copy_ubit{i16}} 101 | export{'simd_copy_1u_i32', copy_ubit{i32}} 102 | export{'simd_copy_1u_f64', copy_ubit{f64}} 103 | -------------------------------------------------------------------------------- /src/utils/hash.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #define HASH_C 1 3 | #include "hash.h" 4 | #include "time.h" 5 | 6 | B asNormalized(B x, usz n, bool nanBad); // from search.c 7 | NOINLINE u64 bqn_hashObj(B x, const u64 secret[4]) { // TODO manual separation of atom & arr probably won't be worth it when there are actually sane typed array hashing things 8 | if (isArr(x)) { 9 | usz xia = IA(x); 10 | if (xia==0) return ~secret[3]; // otherwise squeeze will care about fills 11 | x = squeeze_any(incG(x)); 12 | u8 xr = RNK(x); 13 | u8 xe = TI(x,elType); 14 | u64 shHash; 15 | if (xr<=1) shHash = wyhash64(xia, xe); 16 | else shHash = wyhash(SH(x), xr*sizeof(usz), xe, secret); 17 | bool isTemp = false; 18 | void* data; 19 | u64 bytes; 20 | switch(xe) { default: UD; 21 | case el_bit: 22 | #if TEST_BAD_HASH 23 | if (xia>=64) return *(u64*)bitany_ptr(x); 24 | #endif 25 | bcl(x,xia); bytes = (xia+7)>>3; data = bitany_ptr(x); break; 26 | case el_i8: case el_c8: bytes = xia*1; data = tyany_ptr(x); break; 27 | case el_i16: case el_c16: bytes = xia*2; data = tyany_ptr(x); break; 28 | case el_i32: case el_c32: bytes = xia*4; data = tyany_ptr(x); break; 29 | case el_f64: bytes = xia*8; 30 | x = asNormalized(x, xia, false); 31 | data = f64any_ptr(x); 32 | break; 33 | case el_B:; 34 | data = TALLOCP(u64, xia); 35 | isTemp = true; 36 | SGetU(x) 37 | for (usz i = 0; i < xia; i++) ((u64*)data)[i] = bqn_hash(GetU(x, i), secret); 38 | bytes = xia*sizeof(B); 39 | break; 40 | } 41 | assert(bytes!=0); 42 | u64 r = wyhash(data, bytes, shHash, secret); 43 | if (isTemp) TFREE(data); 44 | decG(x); 45 | return r; 46 | } 47 | 48 | u64 hashbuf[3]; 49 | switch(TY(x)) { 50 | case t_funBl: case t_md1Bl: case t_md2Bl: 51 | case t_funBI: case t_md1BI: case t_md2BI: 52 | IF_WRAP(case t_funWrap: case t_md1Wrap: case t_md2Wrap:) 53 | case t_ns: case t_nfn: case t_comp: // t_comp for profiler 54 | return wyhash64(secret[0], x.u); 55 | 56 | case t_md1D: { 57 | Md1D* xv = c(Md1D,x); 58 | hashbuf[0] = 0; 59 | hashbuf[1] = bqn_hash(xv->f, secret); 60 | hashbuf[2] = bqn_hash(tag(xv->m1, MD1_TAG), secret); 61 | break; 62 | } 63 | case t_md2D: { 64 | Md2D* xv = c(Md2D,x); 65 | hashbuf[0] = bqn_hash(xv->g, secret); 66 | hashbuf[1] = bqn_hash(xv->f, secret); 67 | hashbuf[2] = bqn_hash(tag(xv->m2, MD2_TAG), secret); 68 | break; 69 | } 70 | case t_fork: { 71 | Fork* xv = c(Fork,x); 72 | hashbuf[0] = bqn_hash(xv->f, secret); 73 | hashbuf[1] = bqn_hash(xv->g, secret); 74 | hashbuf[2] = bqn_hash(xv->h, secret); 75 | break; 76 | } 77 | case t_atop: { 78 | Atop* xv = c(Atop,x); 79 | hashbuf[0] = 0; 80 | hashbuf[1] = bqn_hash(xv->g, secret); 81 | hashbuf[2] = bqn_hash(xv->h, secret); 82 | break; 83 | } 84 | default: /*printf("%d/%s\n",TY(x),type_repr(TY(x)));*/ thrM("Cannot hash this object"); 85 | } 86 | return wyhash(hashbuf, sizeof(hashbuf), TY(x), secret); 87 | } 88 | 89 | 90 | INIT_GLOBAL u64 wy_secret[4]; 91 | 92 | void hash_init(void) { 93 | u64 bad1=0xa0761d6478bd642full; // values wyhash64 is afraid of 94 | u64 bad2=0xe7037ed1a0b428dbull; 95 | #if HASHSEED 96 | u64 seed = HASHSEED; 97 | #else 98 | u64 seed = nsTime(); 99 | #endif 100 | again: 101 | make_secret(seed++, wy_secret); 102 | for (u64 i = 0; i < 4; i++) if(wy_secret[i]==bad1 || wy_secret[i]==bad2) goto again; 103 | } 104 | -------------------------------------------------------------------------------- /test/cases/hash.bqn: -------------------------------------------------------------------------------- 1 | %DEF eqvar ⟨_eqvar⟩←⊑•args 2 | 3 | # some checks for behavior around ¯0; not strictly necessary to stay, but other tests will probably become meaningless without these! 4 | 1⊏64‿1•bit._cast ≍˘ •ParseFloat¨ "1.2"‿"-0" %% 63‿1/0‿1 5 | ! (4⥊0) (≡ ∧ ≢○(64‿1•bit._cast⊢)) 1↓¯1×π∾4⥊0 6 | ! ≢˝64‿1•bit._cast ≍˘0‿5⊏⥊(¯1⊸×≍⊢) (↕3)∾∞∾0÷0 7 | 8 | # ¯0 9 | ! ≡´ {•Hash •ParseFloat¨ 𝕩‿"1.2"}¨ "0"‿"-0" 10 | ! 1‿0 ≡ ∊{•ParseFloat¨ 𝕩‿"1.2"}¨ "0"‿"-0" 11 | a←(•ParseFloat¨ "1.2"‿"-0"‿"0")∾-⊸⋈0÷0 ⋄ ∊1•Hash¨ a %% 1‿1‿0‿1‿0 12 | a←(•ParseFloat¨ "1.2"‿"-0"‿"0")∾-⊸⋈0÷0 ⋄ b←0+--a ⋄ ! a •internal.EEqual b ⋄ ! a ≡○(3⊸↑) b ⋄ ! 0≡a •Cmp b ⋄ ! a ≡○(1⊸•Hash¨) b ⋄ ! a ≡○(1⊸•Hash) b 13 | a←(•ParseFloat¨ "1.2"‿"-0"‿"0")∾-⊸⋈0÷0 ⋄ ! (0‿3‿3‿3⊏a) ≡○•Hash (0‿4‿4‿4⊏a) 14 | 15 | # functions 16 | ⍷⟨⊢⊢⊢, ⊢⊢⊢, +⊢⊢, ⊢+⊢, ⊢⊢+, ⊢⊢, ⊢⊢, ⊢+, 1⊸+, 1⊸+, 2⊸+, 1⟜+, 1⊸×, ⊢+⊢, ⊢⊢⊢, +⊢⊢, +˘, ע, +˘, +˝⟩ %% ⟨⊢⊢⊢, +⊢⊢, ⊢+⊢, ⊢⊢+, ⊢⊢, ⊢+, 1⊸+, 2⊸+, 1⟜+, 1⊸×, +˘, ע, +˝⟩ 17 | 18 | # various 19 | [1‿1, 0‿1, 1‿0, 0‿0, 1‿1, 0‿0] ∊ [0‿0, 0‿1, 1‿0] %% 0‿1‿1‿1‿0‿1 20 | [[0‿0, 0‿1, 1‿0, 1‿1], [1‿1, 0‿1, 1‿0, 0‿0]] ∊ [0‿0, 0‿1, 1‿0] %% 2‿4⥊1‿1‿1‿0‿0‿1‿1‿1 21 | [1‿1, 0‿1, 1‿0, 0‿0, 1‿1, 0‿0] ∊○(≍⎉0) [0‿0, 0‿1, 1‿0] %% 0‿1‿1‿1‿0‿1 22 | [[0‿0, 0‿1, 1‿0, 1‿1], [1‿1, 0‿1, 1‿0, 0‿0]] ∊○(≍⎉0) [0‿0, 0‿1, 1‿0] %% 2‿4⥊1‿1‿1‿0‿0‿1‿1‿1 23 | ⊐˜∾˜"ihxtIddKLp"‿'r'‿÷‿"EXiRowcDXx"‿'a'‿"ZNtqFbDCYZ"‿⍒‿0.05369125494326554‿↑‿5 %% ∾˜↕10 24 | 0‿1∊˜⟨"ab","cd"⟩ %% 0‿0 25 | 0‿1⊐ ⟨"ab","cd"⟩ %% 2‿2 26 | (<"ab")∊"ab"‿"cd" %% <1 27 | (<"cd")∊"ab"‿"cd" %% <1 28 | (<"ef")∊"ab"‿"cd" %% <0 29 | (127⥊"abc")⊐"abcd" %% 0‿1‿2‿127 30 | (128⥊"abc")⊐"abcd" %% 0‿1‿2‿128 31 | (32767⥊"abc")⊐"abcd" %% 0‿1‿2‿32767 32 | (32768⥊"abc")⊐"abcd" %% 0‿1‿2‿32768 33 | (127⥊"abc")⊐100⥊"ababacadabacaba" %% (100⥊0‿1‿0‿1‿0‿2‿0‿ 127‿0‿1‿0‿2‿0‿1‿0) 34 | (128⥊"abc")⊐100⥊"ababacadabacaba" %% (100⥊0‿1‿0‿1‿0‿2‿0‿ 128‿0‿1‿0‿2‿0‿1‿0) 35 | (32767⥊"abc")⊐100⥊"ababacadabacaba" %% (100⥊0‿1‿0‿1‿0‿2‿0‿32767‿0‿1‿0‿2‿0‿1‿0) 36 | (32768⥊"abc")⊐100⥊"ababacadabacaba" %% (100⥊0‿1‿0‿1‿0‿2‿0‿32768‿0‿1‿0‿2‿0‿1‿0) 37 | ⍷ •internal.Variation⟜(↕1)¨ "Ab"‿"Ai8" %% ⟨⋈0⟩ 38 | %USE eqvar ⋄ 0⊸•Hash _eqvar ↕5 39 | %USE eqvar ⋄ 0⊸•Hash _eqvar "hello" 40 | %USE eqvar ⋄ 0⊸•Hash _eqvar ↕⋈10 41 | 42 | {b←•internal.Unshare a←"Ai8"•internal.Variation ∾⟨16⥊0‿0‿0‿0‿0‿0‿0‿¯128, 8⥊0, 8↑2⟩ ⋄ c←8‿64•bit._cast a ⋄ ! 1‿0‿0‿1≡∊c ⋄ •internal.Keep •Hash c ⋄ ! a≡b} # make sure sign bit doesn't mutate away 43 | 44 | # error messages 45 | !"𝕨∊𝕩: 𝕩 cannot have rank 0" % 1‿2‿3∊0 46 | !"𝕨⊐𝕩: 𝕨 cannot have rank 0" % 0⊐1‿2‿3 47 | !"𝕨⊒𝕩: 𝕨 cannot have rank 0" % 0⊒1‿2‿3 48 | !"𝕨∊𝕩: Rank of 𝕨 must be at least the cell rank of 𝕩 (⟨2⟩ ≡ ≢𝕨, 2‿2‿2 ≡ ≢𝕩)" % 1‿2∊2‿2‿2⥊1‿2 49 | !"𝕨⊐𝕩: Rank of 𝕩 must be at least the cell rank of 𝕨 (2‿2‿2 ≡ ≢𝕨, ⟨2⟩ ≡ ≢𝕩)" % (2‿2‿2⥊1‿2)⊐1‿2 50 | !"𝕨⊒𝕩: Rank of 𝕩 must be at least the cell rank of 𝕨 (2‿2‿2 ≡ ≢𝕨, ⟨2⟩ ≡ ≢𝕩)" % (2‿2‿2⥊1‿2)⊒1‿2 51 | 52 | # hashmap 53 | ("abc"‿"de"‿"fgh" •HashMap ⥊¨↕3).Get "fgh" %% ⟨2⟩ 54 | ∞ ("abc"‿"de"‿"fgh" •HashMap ⥊¨↕3).Get "fghi" %% ∞ 55 | ("abc"‿"de"‿"fgh" •HashMap ⥊¨↕3).Count"xy" %% 3 56 | ("b" ("a"‿"b" •HashMap "A"‿"C").Set "B").Get "b" %% "B" 57 | ((•HashMap˜↕4).Set´ 1‿"one").Keys↕2 %% ↕4 58 | (((•HashMap˜↕4).Delete 1).Set´ 1‿"one").Keys@ %% 0‿2‿3‿1 59 | (((•HashMap˜↕4).Delete 1).Set´ 1‿"one").Values@ %% 0‿2‿3‿"one" 60 | ({𝕩.Set´"xy"}⍟4•HashMap˜↕0).Count@ %% 1 61 | !"𝕨 •HashMap 𝕩: Arguments must be lists (⟨⟩≡≢𝕨, ⟨3⟩≡≢𝕩)" % 'a' •HashMap "str" 62 | !"𝕨 •HashMap 𝕩: 𝕨 and 𝕩 must have the same length (4≡≠𝕨, 3≡≠𝕩)" % "stri" •HashMap "str" 63 | !"•HashMap: 𝕨 contained duplicate keys" % "strs" •HashMap "stri" 64 | !"(hashmap).Get: key not found" % ("abc"‿"de"‿"fgh" •HashMap ⥊¨↕3).Get "fg" 65 | !"(hashmap).Delete: key not found" % ("abc"‿"de"‿"fgh" •HashMap ⥊¨↕3).Delete 'a' 66 | m ← 1‿2 •HashMap v←•internal.Unshare 'a'‿4 ⋄ 1 m.Set 9 ⋄ ⟨v, m.Keys@, m.Values@⟩ %% ⟨'a'‿4, 1‿2, 9‿4⟩ 67 | m ← 1‿2 •HashMap •internal.Unshare 'a'‿4 ⋄ v←m.Values@ ⋄ 1 m.Set 9 ⋄ ⟨v, m.Keys@, m.Values@⟩ %% ⟨'a'‿4, 1‿2, 9‿4⟩ 68 | -------------------------------------------------------------------------------- /src/singeli/src/avx2.singeli: -------------------------------------------------------------------------------- 1 | # super questionable pack - assumes high halves are zero 2 | def packQQ{a:T,b:T==[4]i64} = vec_shuffle{[4]i32, a, 0,2,1,1} | vec_shuffle{[4]i32, b, 1,1,0,2} 3 | def packQQ{{a, b}} = packQQ{a, b} 4 | 5 | # masked store; F variants may not be a single instruction 6 | def store_masked_top{p:*E, m:M=[k]_, v:V=[k]E if w256{V, 32} and w256i{M}} = emit{void, '_mm256_maskstore_epi32', *i32~~p, m, [8]i32~~v} 7 | def store_masked_top{p:*E, m:M=[k]_, v:V=[k]E if w256{V, 64} and w256i{M}} = emit{void, '_mm256_maskstore_pd', *f64~~p, m, [4]f64~~v} 8 | def store_masked_hom{p:*E, m:M=[k]_, v:V=[k]E if w256{V } and w256i{M}} = store_masked_top{p,m,v} 9 | 10 | def store_blended_top{p:*E, m:M=[k]_, v:V=[k]E if w256i{M} and width{E}>=32} = store_masked_top{p,m,v} 11 | def store_blended_hom{p:*E, m:M=[k]_, v:V=[k]E if w256i{M} and width{E}>=32} = store_masked_top{p,m,v} 12 | def store_blended_hom{p:*E, m:M=[k]_, v:V=[k]E if w256i{M} and width{E}<=16 and w256{V,elwidth{M}}} = store{p, blend_hom{load{V, p}, v, m}} 13 | 14 | # mask stuff 15 | def top_to_int{x:T if w256{T, 8}} = emit{u32, '_mm256_movemask_epi8', x} 16 | def top_to_int{x:T if w256{T, 16}} = { 17 | msk:u32 = top_to_int{emit{[32]u8, '_mm256_packs_epi16', x, [16]u16**0}} 18 | (msk&255) | (msk>>8) 19 | } 20 | 21 | def any_hom{x:T if w256i{T}} = ~emit{u1, '_mm256_testz_si256', v2i{x}, v2i{x}} 22 | def all_hom{x:T if w256i{T}} = hom_to_int{[32]u8 ~~ x} == 0xffff_ffff 23 | def any_top{x:T if w256i{T}} = top_to_int{x} != 0 24 | def all_top{x:T=[k]_ if w256i{T}} = top_to_int{x} == tail{k} 25 | def hom_to_int{a:T, b:T if w256i{T,16}} = hom_to_int{vec_shuffle{[4]u64, packs128{ty_s{a},ty_s{b}}, 0,2,1,3}} 26 | 27 | def any_top{x:T if w256i{T,32}} = ~emit{u1, '_mm256_testz_ps', v2f{x}, v2f{x}} 28 | def any_top{x:T if w256i{T,64}} = ~emit{u1, '_mm256_testz_pd', v2d{x}, v2d{x}} 29 | def any_hom{x:T if w256i{T} and elwidth{T}>=32} = any_top{x} 30 | 31 | def any_top{x:T if w256i{T,16}} = any_hom{[16]i16~~x < [16]i16**0} 32 | def all_top{x:T if w256i{T,16}} = all_hom{[16]i16~~x < [16]i16**0} 33 | 34 | 35 | # conversion 36 | def narrow{T, x:X if w256i{X,32} and width{T}==8} = { 37 | a:= packs128{x, x} 38 | b:= packs128{a, a} 39 | re_el{T, sel{[8]u32, b, make{[8]i32, 0,4,0,4,0,4,0,4}}} 40 | } 41 | def narrow{T, x:X if w256i{X,32} and width{T}==16} = re_el{T, vec_shuffle{[4]u64, packs128{x, x}, 0,2,1,3}} 42 | def narrow{T, x:X if w256i{X,16} and width{T}== 8} = re_el{T, vec_shuffle{[4]u64, packs128{x, x}, 0,2,1,3}} 43 | 44 | def narrow{T, x:X if w256f{X,64} and T>1<<2) | (iota{32}&1)}}} 49 | def narrow{T, x:X if w256u{X,64} and T== u8} = re_el{T, sel{[16]i8, narrow{u32,x}, make{[32]i8, 4*iota{32}}}} 50 | 51 | 52 | def cvt2{(i32), x:X==[4]f64} = emit{[4]i32, '_mm256_cvtpd_epi32', x} 53 | def cvt2{(f64), x:X==[4]i32} = emit{[4]f64, '_mm256_cvtepi32_pd', x} 54 | 55 | 56 | def unzip128{a:[k]E, b:[k]E, 0 if hasarch{'X86_64'} and isunsigned{E}} = { 57 | def ED = w_d{E} 58 | def c = make{[k]E, maxvalue{E} * (1-(1 & range{k}))} 59 | packs128{re_el{ED,a&c}, re_el{ED,b&c}} 60 | } 61 | def unzip128{a:[k]E, b:[k]E, 1 if hasarch{'X86_64'} and isunsigned{E}} = { 62 | def ED = w_d{E} 63 | def ew = width{E} 64 | packs128{re_el{ED,a} >> ew, re_el{ED,b} >> ew} 65 | } 66 | def unzip128{a:V, b:V if vece{V,32}, k if int_idx{k,2}} = vec_shuffle{[4]f32, tup{a,b}, k+tup{0,2,0,2}} 67 | def unzip128{a:V, b:V if vece{V,64}, k if int_idx{k,2}} = vec_shuffle{[2]f64, tup{a,b}, k+tup{0,0}} 68 | 69 | def unzip{a:T, b:T, k if w128u{T} and hasarch{'SSE2'}} = unzip128{a, b, k} 70 | def unzip{a:T, b:T, k if w256u{T} and hasarch{'AVX2'}} = vec_shuffle{[4]u64, unzip128{a, b, k}, 0,2,1,3} 71 | -------------------------------------------------------------------------------- /src/singeli/src/hashtab.singeli: -------------------------------------------------------------------------------- 1 | local include 'skin/cext' 2 | local include './cbqnDefs' # talloc/tfree 3 | 4 | # Search primitives 5 | # Function params are passed as names to keep generated code readable 6 | def names = tup{'memberOf', 'count', 'indexOf'} 7 | def prims = tup{'∊', '⊒', '⊐' } 8 | def map{{f,...fs}, {t,...ts}, v} = if (v==f) t else map{fs, ts, v} 9 | def to_prim = map{names, prims, .} 10 | 11 | # Defined in C 12 | def memset{p:*T, v, l} = { 13 | emit{void, merge{'memset',fmtnat{width{T}}}, p, v, l} 14 | } 15 | 16 | # These hashes are stored in tables and must be invertible! 17 | # Murmur3 18 | def hash_val{x0:(u32)} = { 19 | x := x0 20 | x ^= x >> 16; x *= 0x85ebca6b 21 | x ^= x >> 13; x *= 0xc2b2ae35 22 | x ^= x >> 16; x 23 | } 24 | def hash_val{x0:(u64)} = { 25 | x := x0 26 | x ^= x >> 33; x *= 0xff51afd7ed558ccd 27 | x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53 28 | x ^= x >> 33; x 29 | } 30 | # CRC32 31 | if_inline (hasarch{'SSE4.2'}) require{'x86intrin.h'} 32 | def hash_val{x:(u32) if hasarch{'SSE4.2'}} = { 33 | emit{u32, '_mm_crc32_u32', 0x973afb51, x} 34 | } 35 | 36 | # Allocate and initialize resizing hash table 37 | # Initial size sz+ext and maximum size msz+ext 38 | # One region for each type in Ts initialized with value from v0s 39 | # Allocates the maximum at the start, resizes downwards within the existing allocation 40 | def hash_alloc{logsz, msz, ext, Ts, v0s, has_radix, ordered} = { 41 | def ws = each{width,Ts} 42 | def wt = select{ws,0} 43 | each{assert, slice{ws,0,-1} >= slice{ws,1}} # Doesn't do alignment 44 | # Variables updated on resize 45 | sz := usz~~1 << logsz 46 | sh := wt - logsz 47 | 48 | def add{}=0; def add{a,...r} = a+add{...r} 49 | halloc := talloc{u8, (msz+ext)*add{...ws/8}} 50 | szo := msz-sz # Beginning of allocation to initial table 51 | sze := sz+ext # Initial table to end of allocation 52 | def pe{{}} = halloc; def pl{{}} = tup{} 53 | def pe{{..._, p}} = p+sze # Next unallocated space given pointers so far 54 | def pl{{...R, T}} = { def ps=pl{R}; tup{...ps, *T~~pe{ps}+szo} } 55 | ptrs := pl{Ts} 56 | def memset{_, 'any', _} = {} # Indicates initialization not needed 57 | each{memset{., ., sze}, ptrs, v0s} 58 | 59 | def hash_resize{cc, m} = { 60 | dif := sz*tail{m} # Number of elements of space to add 61 | sh -= m; sz <<= m 62 | set_thresh{} 63 | cc = 0 # Collision counter 64 | k:select{Ts,0} = 0; --k # Index where to move an element to 65 | each{{p,v} => { p -= dif; memset{p, v, dif} }, ptrs, v0s} 66 | def {hash, ...vals} = ptrs; def {h0, ...v0r} = v0s 67 | s := sz+ext 68 | i := dif 69 | 70 | # Iterate over existing elements in blocks of e<=32 71 | # Branchlessly skips over h0 72 | while (i < s) { 73 | e := __min{s-i, usz~~32} 74 | b:u32 = 0 75 | @for_backwards (h in hash+i over e) b = 2*b + promote{u32, h!=h0} 76 | while (b!=0) { 77 | j := i + cast_i{usz,ctz{b}}; b &= b-1 78 | h := hash->j 79 | hash <-{j} h0 80 | k0 := h>>sh 81 | if (ordered) { k = __max{k0, k+1} } # k0 can't be less than k 82 | else { k = k0; while (hash->k!=h0) ++k } 83 | cc += cast_i{ux, k-k0} 84 | hash <-{k} h 85 | def move{p,v0} = { 86 | v := p->j; if (not same{v0,'any'}) p <-{j} v0; p <-{k} v 87 | } 88 | each{move, vals, v0r} 89 | } 90 | i += e 91 | } 92 | } 93 | 94 | # Test for resize if more than 1/2^t collisions/element 95 | t:usz = 0 # Shift amount 96 | def div_thresh{i} = i>>t 97 | # Threshold setter, re-applied on resize 98 | def set_thresh{} = { 99 | if (sz==msz) t = 0 100 | else if ((not has_radix) and sz>=(1<<24)/wt) t = 0 101 | else if ( sz>=(1<<20)/wt) t = 3 102 | else t = 5 103 | } 104 | tup{set_thresh, div_thresh} 105 | set_thresh{} 106 | 107 | tup{ptrs, sz, sh, div_thresh, hash_resize, {}=>tfree{halloc}} 108 | } 109 | -------------------------------------------------------------------------------- /src/singeli/src/sse.singeli: -------------------------------------------------------------------------------- 1 | # load & store 2 | def load{V=[_]E, ptr:*E, vl if w128{V} and vl*width{E}==16} = V ~~ emit{[16]u8, '_mm_loadu_si16', ptr} 3 | def load{V=[_]E, ptr:*E, vl if w128{V} and vl*width{E}==32} = V ~~ emit{[16]u8, '_mm_loadu_si32', ptr} 4 | def load{V=[_]E, ptr:*E, vl if w128{V} and vl*width{E}==64} = V ~~ emit{[16]u8, '_mm_loadu_si64', ptr} 5 | 6 | def store{ptr:*E, x:V=[_]E, vl if w128{V} and vl*width{E}==16} = emit{void, '_mm_storeu_si16', ptr, v2i{x}} 7 | def store{ptr:*E, x:V=[_]E, vl if w128{V} and vl*width{E}==32} = emit{void, '_mm_storeu_si32', ptr, v2i{x}} 8 | def store{ptr:*E, x:V=[_]E, vl if w128{V} and vl*width{E}==64} = emit{void, '_mm_storeu_si64', ptr, v2i{x}} 9 | 10 | 11 | 12 | # integer arith 13 | def __mul{a:T, b:T if w128i{T, 32}} = { 14 | def mu{x, y} = [4]i32 ~~ mul32{[2]u64~~x, [2]u64~~y} 15 | def sw{n, ...vs} = each{{c} => vec_shuffle{i32, c, n}, vs} 16 | lo:= mu{a, b} 17 | hi:= mu{...sw{tup{1,0}, a, b}} 18 | [4]i32~~mzip{...sw{tup{0,2,1,3}, lo, hi}, 0} 19 | } 20 | 21 | # float arith 22 | def rsqrtE{a:([4]f32)} = emit{[4]f32, '_mm_rsqrt_ps', a} 23 | def rcpE{a:([4]f32)} = emit{[4]f32, '_mm_rcp_ps', a} 24 | 25 | # mask stuff 26 | def and_bit_none{x:T, y:T if w128i{T}} = all_hom{(x & y) == T**0} 27 | 28 | def top_to_int{x:T if w128{T, 8}} = emit{u16, '_mm_movemask_epi8', x} 29 | def top_to_int{x:T if w128{T, 16}} = top_to_int{packs{[8]i16~~x, [8]i16**0}} 30 | def top_to_int{x:T if w128{T, 32}} = emit{u8, '_mm_movemask_ps', v2f{x}} 31 | def top_to_int{x:T if w128{T, 64}} = emit{u8, '_mm_movemask_pd', v2d{x}} 32 | def hom_to_int{x:T if w128{T}} = top_to_int{x} 33 | def hom_to_int_ext{a:[_]T if width{T}==16} = tup{2, hom_to_int{re_el{u8,a}}} 34 | def hom_to_int{a:T, b:T if w128i{T,16}} = hom_to_int{packs{ty_s{a},ty_s{b}}} 35 | 36 | def any_hom{x:T if w128i{T}} = hom_to_int{[16]u8 ~~ x} != 0 37 | def all_hom{x:T if w128i{T}} = hom_to_int{[16]u8 ~~ x} == 0xffff 38 | 39 | def any_top{x:T if w128i{T}} = top_to_int{x} != 0 40 | def all_top{x:T=[k]_ if w128i{T}} = top_to_int{x} == tail{k} 41 | def any_top{x:T if w128i{T, 16}} = any_hom{[8]i16~~x < [8]i16**0} 42 | def all_top{x:T if w128i{T, 16}} = all_hom{[8]i16~~x < [8]i16**0} 43 | 44 | def store_blended_hom{p:*E, m:M=[k]_, v:V=[k]E if w128i{M,width{E}} and w128{V}} = store{p, blend_hom{load{V, p}, v, m}} 45 | 46 | def widen{T, x:X if w128i{T} and w128i{X} and w128s{T}==w128s{X} and elwidth{T}>elwidth{X}} = { 47 | def s{v} = s{mzip{v,v,0}} 48 | def s{v:(T)} = v 49 | s{x} >> (elwidth{T} - elwidth{X}) 50 | } 51 | def widen{T==[2]f64, x:X if w128s{X} and elwidth{X}<32} = widen{T, widen{[4]i32, x}} 52 | def widen{T==[2]f64, x:X==[4]i32} = emit{T, '_mm_cvtepi32_pd', x} 53 | def widen{T==[2]f64, x:X==[4]f32} = emit{T, '_mm_cvtps_pd', x} 54 | 55 | local def nsh = tup{0,2,3,3} 56 | local def nar = vec_shuffle{..., nsh} 57 | def narrow{T==i16, x:([4]i32)} = packs{x,x} 58 | def narrow{T==i8, x:([8]i16)} = packs{x,x} 59 | def narrow{T==u8, x:([8]u16)} = packs{x,x} 60 | def narrow{T==u16, x:([4]u32)} = [8]u16~~nar{i32, nar{i16, x}} 61 | def narrow{T==i8, x:([4]i32)} = narrow{T, narrow{i16, x}} 62 | def narrow{T==u8, x:([4]u32)} = { def f{v} = narrow{u8, [8]u16~~v}; f{f{x}}} 63 | def narrow{T==u8, x:([2]u64)} = { def f{v} = narrow{u8, [8]u16~~v}; f{f{f{x}}}} 64 | def narrow{T==u16, x:([2]u64)} = vec_shuffle16_lo{[8]u16~~nar{i32, x}, nsh} 65 | def narrow{T==u32, x:([2]u64)} = [4]u32~~nar{i32, x} 66 | 67 | def narrow{T, x:X if w128f{X,64} and T1) ptr_dec(shObj(x)); 17 | mm_free(v(x)); 18 | } 19 | 20 | 21 | 22 | static Arr* m_hslice(Arr* p, B* ptr, usz ia) { 23 | HSlice* r = m_arr(sizeof(HSlice), t_hslice, ia); 24 | r->p = p; 25 | r->a = ptr; 26 | return (Arr*)r; 27 | } 28 | static Arr* harr_slice (B x, usz s, usz ia) { return m_hslice(a(x), c(HArr,x)->a+s, ia); } 29 | static Arr* hslice_slice(B x, usz s, usz ia) { Arr* p = ptr_inc(c(Slice,x)->p); Arr* r = m_hslice(p, c(HSlice,x)->a+s, ia); decG(x); return r; } 30 | 31 | static B harr_get (Arr* x, usz n) { assert(PTY(x)==t_harr && na; // don't use harrv_ptr so type isn't checked 38 | usz ia = PIA((Arr*)x); 39 | for (usz i = 0; i < ia; i++) dec(p[i]); 40 | } 41 | static void harr_visit(Value* x) { 42 | VISIT_SHAPE(x); 43 | usz ia = PIA((Arr*)x); B* p = harrv_ptr(x); 44 | for (usz i = 0; i < ia; i++) mm_visit(p[i]); 45 | } 46 | static bool harr_canStore(B x) { return true; } 47 | 48 | 49 | 50 | DEF_FREE(harrP) { assert(PTY(x)==t_harrPartial|PTY(x)==t_freed); 51 | B* p = ((HArr*)x)->a; // don't use harrv_ptr so type isn't checked 52 | usz am = PIA((HArr*)x); 53 | for (usz i = 0; i < am; i++) dec(p[i]); 54 | } 55 | void harr_abandon_impl(HArr* p) { assert(PTY(p) == t_harrPartial); 56 | gsPop(); 57 | harrP_freeO((Value*) p); 58 | mm_free((Value*) p); 59 | } 60 | static void harrP_visit(Value* x) { assert(PTY(x) == t_harrPartial); 61 | B* p = harrv_ptr(x); 62 | usz am = PIA((HArr*)x); 63 | for (usz i = 0; i < am; i++) mm_visit(p[i]); 64 | } 65 | static B harrP_get(Arr* x, usz n) { fatal("getting item from t_harrPartial"); } 66 | static void harrP_print(FILE* f, B x) { 67 | B* p = c(HArr,x)->a; 68 | usz am = *SH(x); 69 | usz ia = IA(x); 70 | fprintf(f, "(partial HArr "N64d"/"N64d": ⟨", (u64)am, (u64)ia); 71 | for (usz i = 0; i < ia; i++) { 72 | if (i) fprintf(f, ", "); 73 | if (i>=am) fprintf(f, "?"); 74 | else fprintI(f, p[i]); 75 | } 76 | fprintf(f, "⟩)"); 77 | } 78 | 79 | #if DEBUG 80 | static void harr_freeT(Value* x) { 81 | B* p = harrv_ptr(x); 82 | usz ia = PIA((Arr*)x); 83 | for (usz i = 0; i < ia; i++) assert(!isVal(p[i])); 84 | tyarr_freeF(x); 85 | } 86 | #endif 87 | 88 | void harr_init(void) { 89 | TIi(t_harr,get) = harr_get; TIi(t_hslice,get) = hslice_get; TIi(t_harrPartial,get) = harrP_get; 90 | TIi(t_harr,getU) = harr_getU; TIi(t_hslice,getU) = hslice_getU; TIi(t_harrPartial,getU) = harrP_get; 91 | TIi(t_harr,slice) = harr_slice; TIi(t_hslice,slice) = hslice_slice; 92 | TIi(t_harr,freeO) = harr_freeO; TIi(t_hslice,freeO) = slice_freeO; TIi(t_harrPartial,freeO) = harrP_freeO; 93 | TIi(t_harr,freeF) = harr_freeF; TIi(t_hslice,freeF) = slice_freeF; TIi(t_harrPartial,freeF) = harrP_freeF; 94 | #if DEBUG 95 | TIi(t_harr,freeT) = harr_freeT; 96 | #else 97 | TIi(t_harr,freeT) = tyarr_freeF; 98 | #endif 99 | TIi(t_harr,visit) = harr_visit; TIi(t_hslice,visit) = slice_visit; TIi(t_harrPartial,visit) = harrP_visit; 100 | TIi(t_harr,print) = farr_print; TIi(t_hslice,print) = farr_print; TIi(t_harrPartial,print) = harrP_print; 101 | TIi(t_harr,isArr) = true; TIi(t_hslice,isArr) = true; 102 | TIi(t_harr,canStore) = harr_canStore; 103 | bi_emptyHVec = m_harrUv(0).b; gc_add(bi_emptyHVec); 104 | } 105 | -------------------------------------------------------------------------------- /src/singeli/src/equal.singeli: -------------------------------------------------------------------------------- 1 | include './base' 2 | include './cbqnDefs' 3 | include './f64' 4 | include './mask' 5 | include './bitops' 6 | 7 | def swap{w,x} = { 8 | t:= w 9 | w = x 10 | x = t 11 | } 12 | 13 | # width{W} ≤ width{X} 14 | fn equal{W, X}(w:*void, x:*void, l:u64, d:u64) : u1 = { 15 | def vw = arch_defvw{} 16 | def bulk = vw / width{X} 17 | if (W!=X) if (d!=0) swap{w,x} 18 | assert{l>0} 19 | 20 | if (W==u1) { 21 | if (X==u1) { # bitarr ≡ bitarr 22 | def BT = [vw/8]u8 23 | @for_masked{vw}(w in *BT~~w, x in *BT~~x, M in 'm' over l) if (anyne_bit{w,x,M}) return{0} 24 | } else if (X==f64) { # bitarr ≡ f64arr 25 | def TF = [vw/64]f64 26 | def TU = [vw/64]u64 27 | f0:= TF**0.0 28 | f1:= TF**1.0 29 | @for_masked_pos{bulk}(M in 'm' over i to l) { 30 | wu:= (if (hasarch{'AVX2'}) { 31 | cw:= load_bits_lo{bulk, *u64~~w, i} 32 | blend_top{f0, f1, TU**cw << make{TU,63-iota{vcount{TU}}}} 33 | } else { 34 | cw:= load_expand_bits{TU, *u64~~w, i} 35 | blend_hom{f0, f1, cw} 36 | }) 37 | cx:= load{*TF ~~ x, i} 38 | if (anyne_positive{wu, cx, M}) return{0} 39 | } 40 | } else { # bitarr ≡ i8/i16/i32arr 41 | def T = [bulk]X 42 | def sh{c} = c << (width{X}-1) 43 | def sh{c if X==u8} = T ~~ (re_el{u16,c}<<7) 44 | def mask{x:X if hasarch{'X86_64'}} = top_to_int{x} 45 | def mask{x:X if hasarch{'AARCH64'}} = hom_to_int{andnz{x, ~T**0}} 46 | 47 | # TODO compare with doing the comparison in vector registers 48 | badBits:= T ** ~(X~~1) 49 | @for_masked{bulk}(cw in tup{'b',w}, x in *T~~x, M in 'm' over i to l) { 50 | if (~and_bit_none{M{x}, badBits}) return{0} 51 | if (anyne{promote{u64,mask{sh{x}}}, promote{u64,cw}, M}) return{0} 52 | } 53 | 1 54 | } 55 | } else { # everything not involving bitarrs (i.e. floats/ints, and chars) 56 | if (W==i8 and X==i8) l<<= d 57 | 58 | def R = [bulk]X 59 | 60 | @for_masked_pos{bulk}(M in 'm' over i to l) { 61 | cw:= load_widen{*W~~w, i, R} 62 | cx:= load_widen{*X~~x, i, R} 63 | if (anyne_positive{cw,cx,M}) return{0} 64 | } 65 | } 66 | 1 67 | } 68 | 69 | def eq_reflexive{a:V=[k](f64), b:V} = (a==b) | ((a!=a) & (b!=b)) 70 | 71 | def any_ne_reflexive_qnan{M, a:V=[k](f64), b:V} = { # (a==b) | (isQNaN{a} & isQNaN{b}); assumes at least one arg isn't sNaN 72 | def U = ty_u{V} 73 | def t1 = U~~a & U~~b 74 | 75 | def ne = a != b 76 | def t2 = ne & U**0x7FF8_0000_0000_0000 77 | 78 | def andn_bit_none{M, x:T, y:T} = ~any_bit{M{x&~y}} 79 | def andn_bit_none{M, x:T, y:T if M{0}==0 and hasarch{'X86_64'}} = andn_bit_none{x,y} 80 | ~andn_bit_none{M, t2, t1} 81 | } 82 | 83 | fn equal_reflexive{}(w:*void, x:*void, l:u64, d:u64) : u1 = { 84 | def w = *f64~~w 85 | def x = *f64~~x 86 | def bulk = arch_defvw{} / width{f64} 87 | def unr = if (hasarch{'AARCH64'}) 2 else 1 88 | def V = [bulk]f64 89 | @for_mu{bulk, unr}(w in tup{V,w}, x in tup{V,x}, M in 'm' over i to l) { 90 | if (hasarch{'SSE4.1'}) { 91 | each{{w,x} => { 92 | if (any_ne_reflexive_qnan{M,w,x}) return{0} 93 | }, w, x} 94 | } else { 95 | if (any_hom{M, ...each{{w,x} => ~eq_reflexive{w,x}, w, x}}) return{0} 96 | } 97 | } 98 | 1 99 | } 100 | 101 | export{'simd_equal_1_1', equal{u1, u1}} 102 | export{'simd_equal_1_8', equal{u1, u8}} 103 | export{'simd_equal_1_16', equal{u1, u16}} 104 | export{'simd_equal_1_32', equal{u1, u32}} 105 | export{'simd_equal_1_f64', equal{u1, f64}} 106 | 107 | export{'simd_equal_8_8', equal{i8, i8}} 108 | 109 | export{'simd_equal_s8_16', equal{i8, i16}} 110 | export{'simd_equal_s8_32', equal{i8, i32}} 111 | export{'simd_equal_s16_32', equal{i16, i32}} 112 | 113 | export{'simd_equal_s8_f64', equal{i8, f64}} 114 | export{'simd_equal_s16_f64', equal{i16, f64}} 115 | export{'simd_equal_s32_f64', equal{i32, f64}} 116 | export{'simd_equal_f64_f64', equal{f64, f64}} 117 | export{'simd_equal_f64_f64_reflexive', equal_reflexive{}} 118 | 119 | export{'simd_equal_u8_16', equal{u8, u16}} 120 | export{'simd_equal_u8_32', equal{u8, u32}} 121 | export{'simd_equal_u16_32', equal{u16, u32}} 122 | -------------------------------------------------------------------------------- /src/core/tyarr.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #include "../utils/mut.h" 3 | 4 | INIT_GLOBAL u8 const elType2type[] = { 5 | [el_i8 ] = t_i8arr, [el_c8 ] = t_c8arr, 6 | [el_i16] = t_i16arr,[el_c16] = t_c16arr, 7 | [el_i32] = t_i32arr,[el_c32] = t_c32arr, 8 | [el_bit] = t_bitarr,[el_f64] = t_f64arr, 9 | [el_B] = t_invalid, [el_MAX] = t_invalid 10 | }; 11 | INIT_GLOBAL u8 const elTypeWidth[] = { 12 | [el_i8 ] = 1, [el_c8 ] = 1, 13 | [el_i16] = 2, [el_c16] = 2, 14 | [el_i32] = 4, [el_c32] = 4, 15 | [el_f64] = 8, [el_B ] = 8, [el_bit] = 0 16 | }; 17 | INIT_GLOBAL u8 const elwBitLogT[] = { 18 | [el_i8 ] = 3, [el_c8 ] = 3, 19 | [el_i16] = 4, [el_c16] = 4, 20 | [el_i32] = 5, [el_c32] = 5, 21 | [el_f64] = 6, [el_B ] = 6, [el_bit] = 0 22 | }; 23 | INIT_GLOBAL u8 const elwByteLogT[] = { 24 | [el_i8 ] = 0, [el_c8 ] = 0, 25 | [el_i16] = 1, [el_c16] = 1, 26 | [el_i32] = 2, [el_c32] = 2, 27 | [el_f64] = 3, [el_B ] = 3, [el_bit] = 99 28 | }; 29 | INIT_GLOBAL u8 const arrTypeWidthLog[] = { 30 | [t_bitarr]=0, // 0 for mut.c hack to allow restoring a bitarr offset from array pointer & arrTypeWidthLog-"corrected" element pointer 31 | [t_i8arr ]=0, [t_i8slice ]=0, [t_c8arr ]=0, [t_c8slice ]=0, 32 | [t_i16arr]=1, [t_i16slice]=1, [t_c16arr]=1, [t_c16slice]=1, 33 | [t_i32arr]=2, [t_i32slice]=2, [t_c32arr]=2, [t_c32slice]=2, 34 | [t_f64arr]=3, [t_f64slice]=3, 35 | [t_harr ]=3, [t_hslice ]=3, [t_fillarr]=3,[t_fillslice]=3 36 | }; 37 | INIT_GLOBAL u8 const arrTypeBitsLog[] = { 38 | [t_bitarr]=0, 39 | [t_i8arr ]=3, [t_i8slice ]=3, [t_c8arr ]=3, [t_c8slice ]=3, 40 | [t_i16arr]=4, [t_i16slice]=4, [t_c16arr]=4, [t_c16slice]=4, 41 | [t_i32arr]=5, [t_i32slice]=5, [t_c32arr]=5, [t_c32slice]=5, 42 | [t_f64arr]=6, [t_f64slice]=6, 43 | [t_harr ]=6, [t_hslice ]=6, [t_fillarr]=6,[t_fillslice]=6 44 | }; 45 | 46 | #define TU I8 47 | #define TP(W,X) W##i8##X 48 | #include "tyarrTemplate.c" 49 | #define TU I16 50 | #define TP(W,X) W##i16##X 51 | #include "tyarrTemplate.c" 52 | #define TU I32 53 | #define TP(W,X) W##i32##X 54 | #include "tyarrTemplate.c" 55 | #define TU C8 56 | #define TP(W,X) W##c8##X 57 | #include "tyarrTemplate.c" 58 | #define TU C16 59 | #define TP(W,X) W##c16##X 60 | #include "tyarrTemplate.c" 61 | #define TU C32 62 | #define TP(W,X) W##c32##X 63 | #include "tyarrTemplate.c" 64 | #define TU F64 65 | #define TP(W,X) W##f64##X 66 | #include "tyarrTemplate.c" 67 | 68 | NOINLINE B m_caf64(usz sz, f64* a) { f64* rp; B r = m_f64arrv(&rp, sz); memcpy(rp, a, sz*sizeof( f64)); return r; } 69 | NOINLINE B m_cai32(usz sz, i32* a) { i32* rp; B r = m_i32arrv(&rp, sz); memcpy(rp, a, sz*sizeof( i32)); return r; } 70 | NOINLINE B m_c8vec(char* a, i64 sz) { u8* rp; B r = m_c8arrv (&rp, sz); memcpy(rp, a, sz*sizeof(char)); return r; } 71 | NOINLINE B m_c32vec(u32* s, i64 sz) { u32* rp; B r = m_c32arrv(&rp, sz); memcpy(rp, s, sz*sizeof( u32)); return r; } 72 | NOINLINE B m_c8vec_0(char* s) { return m_c8vec(s, strlen(s)); } 73 | NOINLINE B m_c32vec_0(u32* s) { usz sz=0; while(s[sz]) sz++; return m_c32vec(s, sz); } 74 | 75 | 76 | static Arr* bitarr_slice(B x, usz s, usz ia) { 77 | u64* rp; Arr* r = m_bitarrp(&rp, ia); 78 | bit_cpy(rp, 0, bitarr_ptr(x), s, ia); 79 | ptr_dec(v(x)); 80 | return r; 81 | } 82 | 83 | static B bitarr_get(Arr* x, usz n) { assert(PTY(x)==t_bitarr && na, n)? m_f64(1) : m_f64(0); } 84 | static bool bitarr_canStore(B x) { return q_bit(x); } 85 | 86 | static void bitarr_init(void) { 87 | TIi(t_bitarr,get) = bitarr_get; 88 | TIi(t_bitarr,getU) = bitarr_get; 89 | TIi(t_bitarr,slice) = bitarr_slice; 90 | TIi(t_bitarr,freeO) = tyarr_freeO; 91 | TIi(t_bitarr,freeF) = tyarr_freeF; 92 | TIi(t_bitarr,visit) = arr_visit; 93 | TIi(t_bitarr,print) = farr_print; 94 | TIi(t_bitarr,isArr) = true; 95 | TIi(t_bitarr,arrD1) = true; 96 | TIi(t_bitarr,elType) = el_bit; 97 | TIi(t_bitarr,canStore) = bitarr_canStore; 98 | } 99 | 100 | INIT_GLOBAL Arr* staticSliceRoot; 101 | void tyarr_init(void) { 102 | i8arr_init(); i16arr_init(); i32arr_init(); bitarr_init(); 103 | c8arr_init(); c16arr_init(); c32arr_init(); f64arr_init(); 104 | 105 | { u64* tmp; gc_add(bi_emptyIVec = m_bitarrv(&tmp, 0)); } 106 | { u8* tmp; gc_add(bi_emptyCVec = m_c8arrv (&tmp, 0)); } 107 | { u8* tmp; gc_add(taga(arr_shVec(staticSliceRoot = m_c8arrp(&tmp, 0)))); } 108 | 109 | Arr* emptySVec = arr_shVec(m_fillarrpEmpty(emptyCVec())); 110 | bi_emptySVec = taga(emptySVec); gc_add(bi_emptySVec); 111 | } 112 | -------------------------------------------------------------------------------- /src/core/fillarr.h: -------------------------------------------------------------------------------- 1 | typedef struct FillArr { 2 | struct Arr; 3 | B fill; 4 | B a[]; 5 | } FillArr; 6 | typedef struct FillSlice { 7 | struct Slice; 8 | B* a; 9 | B fill; 10 | } FillSlice; 11 | 12 | B asFill(B x); // consumes 13 | void validateFill(B x); 14 | 15 | B withFill(B x, B fill); // consumes both 16 | static B qWithFill(B x, B fill) { // consumes both 17 | assert(isArr(x)); 18 | if (noFill(fill) || TI(x,elType)!=el_B) return x; 19 | return withFill(x, fill); 20 | } 21 | 22 | NOINLINE bool fillEqualF(B w, B x); 23 | static bool fillEqual(B w, B x) { 24 | if (w.u==x.u) return true; 25 | if (isAtm(w)|isAtm(x)) return false; 26 | return fillEqualF(w, x); 27 | } 28 | 29 | static bool numFill(B x) { return x.u == m_f64(0).u; } 30 | static bool chrFill(B x) { return isC32(x); } 31 | 32 | static B getFillN(B x) { // doesn't consume, returns unowned value; will return bi_noFill if fill is unknown 33 | if (isArr(x)) { 34 | switch(TI(x,elType)) { default: UD; 35 | case el_i8: case el_i16: case el_i32: case el_f64: case el_bit: return m_i32(0); 36 | case el_c8: case el_c16: case el_c32: return m_c32(' '); 37 | case el_B:; 38 | u8 t = TY(x); 39 | if (t==t_fillarr ) return c(FillArr, x)->fill; 40 | if (t==t_fillslice) return c(FillSlice,x)->fill; 41 | return bi_noFill; 42 | } 43 | } 44 | if (isNum(x)) return m_i32(0); 45 | if (isC32(x)) return m_c32(' '); 46 | return bi_noFill; 47 | } 48 | static B getFillR(B x) { // doesn't consume; will return bi_noFill if fill is unknown 49 | if (isArr(x)) { 50 | switch(TI(x,elType)) { default: UD; 51 | case el_i8: case el_i16: case el_i32: case el_f64: case el_bit: return m_i32(0); 52 | case el_c8: case el_c16: case el_c32: return m_c32(' '); 53 | case el_B:; 54 | u8 t = TY(x); 55 | if (t==t_fillarr ) return inc(c(FillArr, x)->fill); 56 | if (t==t_fillslice) return inc(c(FillSlice,x)->fill); 57 | return bi_noFill; 58 | } 59 | } 60 | if (isNum(x)) return m_i32(0); 61 | if (isC32(x)) return m_c32(' '); 62 | return bi_noFill; 63 | } 64 | static B getFillQ(B x) { // doesn't consume; returns 0 if !SEMANTIC_CATCH 65 | B r = getFillR(x); 66 | #if SEMANTIC_CATCH 67 | return r; 68 | #endif 69 | return noFill(r)? m_f64(0) : r; 70 | } 71 | NORETURN void getFillE_err(B x, char* msg); 72 | static B getFillE(B x, char* msg) { // doesn't consume; errors if there's no fill 73 | NOGC_CHECK("cannot use getFillE during noAlloc"); 74 | B xf = getFillQ(x); 75 | if (noFill(xf)) { 76 | if (PROPER_FILLS) getFillE_err(x, msg); 77 | else return m_f64(0); 78 | } 79 | return xf; 80 | } 81 | 82 | 83 | static Arr* m_fillarrp(usz ia) { // needs a NOGC_E after fill & all elements are initialized 84 | CHECK_IA(ia, sizeof(B)); 85 | Arr* r = m_arr(fsizeof(FillArr,a,B,ia), t_fillarr, ia); 86 | NOGC_S; 87 | return r; 88 | } 89 | static void fillarr_setFill(Arr* x, B fill) { // consumes fill 90 | assert(PTY(x)==t_fillarr); 91 | if (DEBUG) validateFill(fill); 92 | ((FillArr*)x)->fill = fill; 93 | } 94 | static B* fillarrv_ptr (Arr* x) { assert(PTY(x)==t_fillarr); return ((FillArr*)x)->a; } 95 | static B* fillslicev_ptr(Arr* x) { assert(PTY(x)==t_fillslice); return ((FillSlice*)x)->a; } 96 | static Arr* m_fillarrpEmpty(B fill) { 97 | Arr* r = m_fillarrp(0); 98 | fillarr_setFill(r, fill); 99 | NOGC_E; 100 | return r; 101 | } 102 | static Arr* m_fillarr0p(usz ia) { // zero-initialized fillarr, with both fill & elements set to m_f64(0) 103 | Arr* r = arr_shVec(m_fillarrp(ia)); 104 | fillarr_setFill(r, m_f64(0)); 105 | FILL_TO(fillarrv_ptr(r), el_B, 0, m_f64(0), ia); 106 | NOGC_E; 107 | return r; 108 | } 109 | 110 | static UntaggedArr m_barrp_withFill(ux ia, B fill) { // doesn't consume 111 | CHECK_IA(ia, sizeof(B)); 112 | bool has = !noFill(fill); 113 | Arr* r = m_arr(has? fsizeof(FillArr,a,B,ia) : fsizeof(HArr,a,B,ia), has? t_fillarr : t_harr, ia); 114 | if (has) fillarr_setFill(r, fill); 115 | if (ia) NOGC_S; 116 | return (UntaggedArr){r, has? fillarrv_ptr(r) : harrv_ptr(r)}; 117 | } 118 | 119 | 120 | B m_funit(B x); // consumes 121 | B m_unit(B x); // consumes 122 | 123 | static bool fillEqualsGetFill(B fill, B obj) { // returns whether `fill` equals the fill of `obj` 124 | return fillEqual(fill, getFillN(obj)); 125 | } 126 | 127 | static B fill_both(B w, B x) { // doesn't consume 128 | B wf = getFillN(w); 129 | if (noFill(wf)) return bi_noFill; 130 | B xf = getFillR(x); 131 | if (fillEqual(wf, xf)) return xf; 132 | dec(xf); 133 | return bi_noFill; 134 | } 135 | -------------------------------------------------------------------------------- /src/windows/sh.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct { 5 | HANDLE hndl; 6 | char* buf; 7 | u64 len; 8 | } ThreadIO; 9 | 10 | static unsigned int __stdcall winThreadWrite(void* arg0) { 11 | DWORD dwResult = ERROR_SUCCESS; 12 | ThreadIO* arg = arg0; 13 | HANDLE hndl = arg->hndl; 14 | char* wBuf = arg->buf; 15 | DWORD dwToWrite = arg->len, dwWritten = 0, dwOff = 0; 16 | 17 | for (;;) { 18 | BOOL bOk = WriteFile(hndl, &wBuf[dwOff], dwToWrite-dwOff, &dwWritten, NULL); 19 | if (!bOk) { dwResult = GetLastError(); break; } 20 | dwOff += dwWritten; 21 | if (dwOff >= dwToWrite) { break; } 22 | } 23 | 24 | CloseHandle(hndl); 25 | return dwResult; 26 | } 27 | 28 | static unsigned int __stdcall winThreadRead(void* arg0) { 29 | DWORD dwResult = ERROR_SUCCESS; 30 | ThreadIO* arg = arg0; 31 | HANDLE hndl = arg->hndl; 32 | u8 buf[4096] = {0}; 33 | const usz bufSize = sizeof(buf)/sizeof(u8); 34 | DWORD dwRead = 0, dwHasRead = 0; 35 | char* rBuf = NULL; 36 | for (;;) { 37 | ZeroMemory(buf, bufSize); 38 | BOOL bOk = ReadFile(hndl, buf, bufSize, &dwRead, NULL); 39 | if (dwRead == 0) { break; } 40 | if (!bOk) { 41 | dwResult = GetLastError(); 42 | break; 43 | } 44 | char* newBuf = realloc(rBuf, (dwHasRead+dwRead)*sizeof(char)); 45 | if (newBuf == NULL) { dwResult = GetLastError(); break; } 46 | rBuf = newBuf; 47 | memcpy(&rBuf[dwHasRead], buf, dwRead); 48 | dwHasRead += dwRead; 49 | } 50 | 51 | if (dwResult != ERROR_SUCCESS) { 52 | if (rBuf != NULL) { free(rBuf); } 53 | } else { 54 | arg->buf = rBuf; 55 | arg->len = dwHasRead; 56 | } 57 | CloseHandle(hndl); 58 | return dwResult; 59 | } 60 | 61 | static DWORD winCmd(WCHAR* arg, 62 | u64 iLen, char* iBuf, 63 | DWORD* code, 64 | u64* oLen, char** oBuf, 65 | u64* eLen, char** eBuf) { 66 | 67 | DWORD dwResult = ERROR_SUCCESS; 68 | HANDLE hInpR, hInpW; 69 | HANDLE hOutR, hOutW; 70 | HANDLE hErrR, hErrW; 71 | 72 | // Create pipes 73 | SECURITY_ATTRIBUTES sa; 74 | sa.nLength = sizeof(sa); 75 | sa.lpSecurityDescriptor = NULL; 76 | sa.bInheritHandle = TRUE; 77 | 78 | CreatePipe(&hInpR, &hInpW, &sa, 0); 79 | CreatePipe(&hOutR, &hOutW, &sa, 0); 80 | CreatePipe(&hErrR, &hErrW, &sa, 0); 81 | 82 | SetHandleInformation(hInpW, HANDLE_FLAG_INHERIT, 0); 83 | SetHandleInformation(hOutR, HANDLE_FLAG_INHERIT, 0); 84 | SetHandleInformation(hErrR, HANDLE_FLAG_INHERIT, 0); 85 | 86 | // Set up 87 | STARTUPINFOW si; 88 | ZeroMemory(&si, sizeof(si)); 89 | si.cb = sizeof(si); 90 | si.hStdInput = hInpR; 91 | si.hStdOutput = hOutW; 92 | si.hStdError = hErrW; 93 | si.dwFlags |= STARTF_USESTDHANDLES; 94 | 95 | PROCESS_INFORMATION pi; 96 | ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); 97 | 98 | // Create the child process 99 | BOOL bSuccess = CreateProcessW(NULL, arg, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); 100 | if (!bSuccess) { return GetLastError(); } 101 | 102 | // Close the unneeded handles 103 | CloseHandle(hInpR); 104 | CloseHandle(hOutW); 105 | CloseHandle(hErrW); 106 | 107 | // Spawn the thread to deal with redirected io 108 | ThreadIO data0 = {hInpW, iBuf, iLen}; 109 | ThreadIO data1 = {hOutR, NULL, 0}; 110 | ThreadIO data2 = {hErrR, NULL, 0}; 111 | 112 | DWORD exitCode = -1; 113 | HANDLE lpThreads[3]; 114 | lpThreads[0] = (HANDLE)_beginthreadex(NULL, 0, winThreadWrite, (void*)&data0, 0, NULL); 115 | lpThreads[1] = (HANDLE)_beginthreadex(NULL, 0, winThreadRead, (void*)&data1, 0, NULL); 116 | lpThreads[2] = (HANDLE)_beginthreadex(NULL, 0, winThreadRead, (void*)&data2, 0, NULL); 117 | 118 | for (int i = 0; i < 3; ++i) { 119 | if (lpThreads[i] == NULL) { dwResult = GetLastError(); goto error; } 120 | } 121 | 122 | // Wait for the threads 123 | if (WaitForMultipleObjects(3, lpThreads, TRUE, INFINITE) == WAIT_FAILED) { 124 | dwResult = GetLastError(); goto error; 125 | } 126 | for (int i = 0; i < 3; ++i) { 127 | GetExitCodeThread(lpThreads[i], &exitCode); 128 | if (exitCode != ERROR_SUCCESS) { dwResult = exitCode; goto error; } 129 | } 130 | 131 | // Get the exit code 132 | if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) { 133 | dwResult = GetLastError(); goto error; 134 | } 135 | exitCode = -1; 136 | GetExitCodeProcess(pi.hProcess, &exitCode); 137 | 138 | // Give outputs 139 | *code = exitCode; 140 | *oLen = data1.len; *oBuf = data1.buf; 141 | *eLen = data2.len; *eBuf = data2.buf; 142 | 143 | error: 144 | // Close handles 145 | for (int i = 0; i < 3; ++i) { CloseHandle(lpThreads[i]); } 146 | CloseHandle(pi.hProcess); 147 | CloseHandle(pi.hThread); 148 | 149 | return dwResult; 150 | } 151 | -------------------------------------------------------------------------------- /src/core/numarr.h: -------------------------------------------------------------------------------- 1 | typedef struct TyArr { 2 | struct Arr; 3 | char a[]; 4 | } TyArr; 5 | typedef struct TySlice { 6 | struct Slice; 7 | void* a; 8 | } TySlice; 9 | 10 | #define TU I8 11 | #define TP(W,X) W##i8##X 12 | #include "tyarrTemplate.h" 13 | #define TU I16 14 | #define TP(W,X) W##i16##X 15 | #include "tyarrTemplate.h" 16 | #define TU I32 17 | #define TP(W,X) W##i32##X 18 | #include "tyarrTemplate.h" 19 | #define TU F64 20 | #define TP(W,X) W##f64##X 21 | #include "tyarrTemplate.h" 22 | typedef TyArr BitArr; 23 | 24 | // bit array stuff 25 | #define BIT_N(IA) (((IA)+63) >> 6) // u64 count needed to store IA bits 26 | 27 | static inline void bitp_set(u64* arr, u64 n, bool v) { 28 | u8 m = ((u8)1) << (n&7); 29 | if (v) ((u8*)arr)[n>>3]|= m; 30 | else ((u8*)arr)[n>>3]&= ~m; 31 | // arr[n>>6] = (arr[n>>6]&(~m)) | (((u64)v)<<(n&63)); 32 | } 33 | static inline bool bitp_get(u64* arr, u64 n) { 34 | return (((u8*)arr)[n>>3] >> (n&7)) & 1; 35 | } 36 | static inline u64 bitp_l0(u64* arr, u64 ia) { // last u64 of the array, with the tail set to 0s 37 | return ia&63? arr[ia>>6]&((1ULL<<(ia&63))-1) : 0; 38 | } 39 | static inline u64 bitp_l1(u64* arr, u64 ia) { // last u64 of the array, with the tail set to 1s 40 | return ia&63? arr[ia>>6]|~((1ULL<<(ia&63))-1) : ~0ULL; 41 | } 42 | static inline u64 bitx(B x) { // repeats the boolean across all 64 bits 43 | return o2bG(x)? ~(u64)0 : 0; 44 | } 45 | static inline bool bit_has(u64* arr, u64 ia, bool v) { 46 | u64 w = ~-(u64)v; 47 | u64 e = ia/64, q = ia%64; 48 | for (usz i=0; i USZ_MAX) thrOOM() 67 | #else 68 | #define CHECK_BITARR_IA(IA) if ((IA) > USZ_MAX-65) thrOOM() // make sure BIT_N doesn't overflow 69 | #endif 70 | 71 | #define BITARR_SZ(IA) fsizeof(BitArr, a, u64, BIT_N(IA)) 72 | static B m_bitarrv(u64** p, u64 ia) { 73 | CHECK_BITARR_IA(ia); 74 | BitArr* r = m_arr(BITARR_SZ(ia), t_bitarr, ia); 75 | arr_shVec((Arr*)r); 76 | *p = (u64*)r->a; 77 | return taga(r); 78 | } 79 | static B m_bitarrc(u64** p, B x) { assert(isArr(x)); 80 | // no need for a CHECK_BITARR_IA, as bitarrs should have the least restrictive IA requirement 81 | BitArr* r = m_arr(BITARR_SZ(IA(x)), t_bitarr, IA(x)); 82 | *p = (u64*)r->a; 83 | arr_shCopy((Arr*)r, x); 84 | return taga(r); 85 | } 86 | static Arr* m_bitarrp(u64** p, u64 ia) { 87 | CHECK_BITARR_IA(ia); 88 | BitArr* r = m_arr(BITARR_SZ(ia), t_bitarr, ia); 89 | *p = (u64*)r->a; 90 | return (Arr*)r; 91 | } 92 | static u64* bitarrv_ptr(TyArr* x) { VTY(taga(x), t_bitarr); return (u64*)((BitArr*)x)->a; } 93 | static u64* bitanyv_ptr(Arr* x) { VTY(taga(x), t_bitarr); return (u64*)((BitArr*)x)->a; } 94 | static u64* bitarr_ptr(B x) { return bitarrv_ptr(c(TyArr,x)); } 95 | static u64* bitany_ptr(B x) { return bitanyv_ptr(a(x)); } 96 | 97 | 98 | Arr* cpyI8Arr (B x); // consumes 99 | Arr* cpyI16Arr(B x); // consumes 100 | Arr* cpyI32Arr(B x); // consumes 101 | Arr* cpyF64Arr(B x); // consumes 102 | Arr* cpyBitArr(B x); // consumes 103 | 104 | // all consume x 105 | static I8Arr* toI8Arr (B x) { return TY(x)==t_i8arr ? c(I8Arr, x) : (I8Arr*) cpyI8Arr (x); } 106 | static I16Arr* toI16Arr(B x) { return TY(x)==t_i16arr? c(I16Arr,x) : (I16Arr*) cpyI16Arr(x); } 107 | static I32Arr* toI32Arr(B x) { return TY(x)==t_i32arr? c(I32Arr,x) : (I32Arr*) cpyI32Arr(x); } 108 | static F64Arr* toF64Arr(B x) { return TY(x)==t_f64arr? c(F64Arr,x) : (F64Arr*) cpyF64Arr(x); } 109 | static BitArr* toBitArr(B x) { return TY(x)==t_bitarr? c(BitArr,x) : (BitArr*) cpyBitArr(x); } 110 | 111 | static B toI8Any (B x) { u8 t=TY(x); return t==t_i8arr || t==t_i8slice ? x : taga(cpyI8Arr (x)); } 112 | static B toI16Any(B x) { u8 t=TY(x); return t==t_i16arr || t==t_i16slice? x : taga(cpyI16Arr(x)); } 113 | static B toI32Any(B x) { u8 t=TY(x); return t==t_i32arr || t==t_i32slice? x : taga(cpyI32Arr(x)); } 114 | static B toF64Any(B x) { u8 t=TY(x); return t==t_f64arr || t==t_f64slice? x : taga(cpyF64Arr(x)); } 115 | static B toBitAny(B x) { return taga(toBitArr(x)); } 116 | 117 | B m_cai32(usz ia, i32* a); 118 | B m_caf64(usz sz, f64* a); 119 | 120 | i64 bit_sum(u64* x, u64 am); 121 | u64 bit_boundary_up(u64* x, u64 n); // first 0 assuming sorted-up (grade.h) 122 | u64 bit_boundary_dn(u64* x, u64 n); // first 1 assuming sorted-down 123 | u64 usum(B x); // doesn't consume; error if not natural numbers or overflow 124 | -------------------------------------------------------------------------------- /test/cases/fills.bqn: -------------------------------------------------------------------------------- 1 | %DEF fill Fill←•internal.HasFill◶'e'‿{⊑1↑0⥊𝕩} 2 | %DEF eqall EqAll ← {!∘≡⟜(⊑𝕩)¨ 𝕩 ⋄ ⊑𝕩} 3 | 4 | %USE fill ⋄ F←¬ ⋄ ⟨Fill¨ F ⟨"",↕0,↕0‿0⟩, Fill∘F¨ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩⟩ %!HEAPVERIFY %% ⟨⟨' ',0,0‿0⟩,⟨'e',0‿0,0,0,' ',' '⟩⟩ 5 | %USE fill ⋄ F←- ⋄ ⟨Fill¨ F ⟨"",↕0,↕0‿0⟩, Fill∘F¨ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩⟩ %!HEAPVERIFY %% ⟨⟨' ',0,0‿0⟩,⟨'e',0‿0,0,0,' ',' '⟩⟩ 6 | %USE fill ⋄ F←| ⋄ ⟨Fill¨ F ⟨"",↕0,↕0‿0⟩, Fill∘F¨ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩⟩ %!HEAPVERIFY %% ⟨⟨' ',0,0‿0⟩,⟨'e',0‿0,0,0,' ',' '⟩⟩ 7 | 8 | %USE fill ⋄ F←⌊ ⋄ ⟨0,1,{⇐},+,@,'𝕩'⟩ Fill∘F⌜ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩ %!HEAPVERIFY %% 2‿4/[⟨'e',0‿0,0,0,'e','e'⟩,⟨'e','e','e','e','e','e'⟩] 9 | %USE fill ⋄ F←× ⋄ ⟨0,1,{⇐},+,@,'𝕩'⟩ Fill∘F⌜ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩ %!HEAPVERIFY %% 2‿4/[⟨'e',0‿0,0,0,'e','e'⟩,⟨'e','e','e','e','e','e'⟩] 10 | %USE fill ⋄ F←⋆ ⋄ ⟨0,1,{⇐},+,@,'𝕩'⟩ Fill∘F⌜ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩ %!HEAPVERIFY %% 2‿4/[⟨'e',0‿0,0,0,'e','e'⟩,⟨'e','e','e','e','e','e'⟩] 11 | %USE fill ⋄ F←= ⋄ ⟨0,1,{⇐},+,@,'𝕩'⟩ Fill∘F⌜ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩ %!HEAPVERIFY %% 6‿6⥊⟨'e',0‿0,0,0,0,0,'e',0‿0,0,0,0,0,'e','e',0,0,0,0,'e','e',0,0,0,0,'e',0‿0,0,0,0,0,'e',0‿0,0,0,0,0⟩ 12 | %USE fill ⋄ F←+ ⋄ ⟨0,1,{⇐},+,@,'𝕩'⟩ Fill∘F⌜ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩ %!HEAPVERIFY %% 6‿6⥊⟨'e',0‿0,0,0,' ',' ','e',0‿0,0,0,' ',' ','e','e','e','e','e','e','e','e','e','e','e','e','e'," ",' ',' ','e','e','e'," ",' ',' ','e','e'⟩ 13 | %USE fill ⋄ F←- ⋄ ⟨0,1,{⇐},+,@,'𝕩'⟩ Fill∘F⌜ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩ %!HEAPVERIFY %% 6‿6⥊⟨'e',0‿0,0,0,'e','e','e',0‿0,0,0,'e','e','e','e','e','e','e','e','e','e','e','e','e','e','e'," ",' ',' ',0,0,'e'," ",' ',' ',0,0⟩ 14 | %USE fill ⋄ F←-˜ ⋄ ⟨0,1,{⇐},+,@,'𝕩'⟩ Fill∘F⌜ ⟨⟨⟩, ↕0‿0, ↕0, 0‿2⥊↕0, "", 2‿0⥊""⟩ %!HEAPVERIFY %% 6‿6⥊⟨'e',0‿0,0,0,' ',' ','e',0‿0,0,0,' ',' ','e','e','e','e','e','e','e','e','e','e','e','e','e','e','e','e',0,0,'e','e','e','e',0,0⟩ 15 | %USE fill ⋄ (> !∘≡○Fill ⊢)¨ ⟨↕0, ""⟩ 16 | %USE fill ⋄ ⟨⊢,Fill,Fill Fill⟩{𝕎𝕩}¨< 0⥊0‿1⊔2⥊<↕2 %% ⟨⟨⟩,⟨⟩,0‿0⟩ 17 | %USE fill ⋄ Fill¨ 1‿2‿1⊔3‿0⥊0 %% 0‿0‿0 18 | %USE fill ⋄ Fill¨ 1‿2‿1⊔3‿0⥊@ %% " " 19 | %USE fill ⋄ Fill¨ 1‿2‿1⊔3‿0⥊<"abc" %% 3⥊<" " 20 | 21 | !¬•internal.HasFill <"hello"‿+ 22 | %USE fill ⋄ fa ← Fill¨ a←⟨0, 2‿3‿4, 2‿0‿4, 0‿2‿3, 2⟩⥊⟜<⌜⟨0,'a',{⇐},1‿2⟩ ⋄ {𝕊f : fa {!𝕨≡Fill F 𝕩}¨ a}¨ ⟨⥊, 0⊸/, 0⊸⥊, 0⊸↑, 0⊸↓, ⊑∘⋈⟩ ⋄ fa %% 5/≍⟨0,' ','e',0‿0⟩ 23 | 24 | %USE fill ⋄ a←10⥊<1‿2 ⋄ { a∾↩𝕩⋄@}¨100⥊<<1‿2 ⋄ Fill a %% 0‿0 25 | %USE fill ⋄ a←10⥊<1‿2 ⋄ {a↩0↑a ⋄ a∾↩𝕩⋄@}¨100⥊<<1‿2 ⋄ Fill a %% 0‿0 26 | %USE fill ⋄ {𝕊: a←10⥊<1‿2 ⋄ { a∾↩𝕩⋄@}¨(𝕩⥊<<1‿2)∾<<1‿2‿3 ⋄ a!'e'≡Fill a}¨ ↕100 27 | %USE fill ⋄ {𝕊: a←10⥊<1‿2 ⋄ {a↩0↑a ⋄ a∾↩𝕩⋄@}¨(𝕩⥊<<1‿2)∾<<1‿2‿3 ⋄ a!'e'≡Fill a}¨ ↕100 28 | 29 | %USE fill ⋄ Fill ≠¨ ⟨⟩ %!EACH_FILLS %% 'e' 30 | %USE fill ⋄ Fill ≠¨ ⟨⟩ %EACH_FILLS %% 0 31 | %USE fill ⋄ Fill ≠∘⊢¨⟨⟩ %% 'e' 32 | %USE fill ⋄ Fill ⊢¨⟨⟩ %% 'e' 33 | %USE fill ⋄ Fill ⊢∘⊢¨⟨⟩ %% 'e' 34 | 35 | %USE fill ⋄ Fill ≠¨ "" %!EACH_FILLS %% ' ' 36 | %USE fill ⋄ Fill ≠¨ "" %EACH_FILLS %% 0 37 | %USE fill ⋄ Fill ≠∘⊢¨"" %!EACH_FILLS %% ' ' 38 | %USE fill ⋄ Fill ⊢¨"" %!EACH_FILLS %% ' ' 39 | %USE fill ⋄ Fill ⊢∘⊢¨"" %!EACH_FILLS %% ' ' 40 | %USE fill ⋄ Fill ≠∘⊢¨"" %EACH_FILLS %% 0 41 | %USE fill ⋄ Fill ⊢¨"" %EACH_FILLS %% ' ' 42 | %USE fill ⋄ Fill ⊢∘⊢¨"" %EACH_FILLS %% ' ' 43 | 44 | {3↑ -˝𝕩‿2⥊<<'a'}¨ 5‿6 %!HEAPVERIFY %% <¨¨ ⟨"aa ", 3⥊0⟩ 45 | {3↑ (2⥊2)-˝𝕩‿2⥊<<'x'}¨ 5‿6 %!HEAPVERIFY %% <¨¨ ⟨"vv ", 2‿2‿0⟩ 46 | {3↑ "ee"-˝𝕩‿2⥊<<'a'}¨ 5‿6 %!HEAPVERIFY %% <¨¨ ⟨¯4‿¯4‿0, "ee "⟩ 47 | %USE fill ⋄ Fill (↕ 4) ≠˝ "Ah"•internal.Variation 4‿4⥊'a' %% 0 48 | %USE fill ⋄ Fill (↕⋈4) ≠˝ "Ah"•internal.Variation 4‿4⥊'a' %% 'e' 49 | %USE fill ⋄ Fill (↕ 4) ≠˝ 4‿4⥊'a' %% 0 50 | %USE fill ⋄ Fill (↕⋈4) ≠˝ 4‿4⥊'a' %% ⋈0 %!HEAPVERIFY 51 | 52 | %USE fill ⋄ Fill +˝10‿0⥊<0‿0‿0 %% 0‿0‿0 53 | %USE fill ⋄ Fill ⌊˝10‿0⥊<0‿0‿0 %% 0‿0‿0 54 | %USE fill ⋄ Fill ⌈˝10‿0⥊<0‿0‿0 %% 0‿0‿0 55 | %USE fill ⋄ Fill +˝10‿0⥊<"abc" %% 'e' 56 | %USE fill ⋄ Fill ⌊˝10‿0⥊<"abc" %% 'e' 57 | %USE fill ⋄ Fill ⌈˝10‿0⥊<"abc" %% 'e' 58 | 59 | %DEF sqfill SqFill ← {•internal.Squeeze "Ah"•internal.Variation 𝕩} 60 | %USE fill ⋄ %USE eqall ⋄ %USE sqfill ⋄ F←•internal.Keep∘⊢ ⋄ {EqAll Fill¨ ⟨SqFill ⟨𝕩⟩, F¨<𝕩, @F¨<𝕩, @F¨⋈𝕩⟩}¨ ⟨"abc", 4, 'a', {⇐}⟩ 61 | %USE fill ⋄ %USE eqall ⋄ %USE sqfill ⋄ F←•internal.Keep∘⊢ ⋄ {EqAll Fill¨ ⟨SqFill ⥊𝕩, F¨ 𝕩, @F¨ 𝕩 ⟩}¨ ⟨"abc", 4, 'a', {⇐}⟩ 62 | 63 | 2↑ 'a'⊸+¨ 1 %% "b " 64 | 2↑ {⇐} 'a'⊸+¨ 1 %% "b " 65 | 66 | •internal.HasFill ⟨⟩⥊"ab"‿"cde" %% 0 67 | 68 | a←0 ⋄ 0◶{𝕊: a↩1}‿0˘ 0‿0⥊2 ⋄ a %% 0 69 | a←0 ⋄ 0◶{𝕊: a↩1}‿0˘˜0‿0⥊2 ⋄ a %% 0 70 | a←0 ⋄ 0◶{𝕊: a↩1}‿0¨ ↕0 ⋄ a %% 0 71 | a←0 ⋄ 0◶{𝕊: a↩1}‿0¨˜ ↕0 ⋄ a %% 0 72 | a←0 ⋄ 0◶{𝕊: a↩1}‿0⌜ ↕0 ⋄ a %% 0 73 | a←0 ⋄ 0◶{𝕊: a↩1}‿0⌜˜ ↕0 ⋄ a %% 0 74 | 75 | «⟨⟩ %% ⟨⟩ 76 | »⟨⟩ %% ⟨⟩ 77 | «4‿0⥊⟨⟩ %% 4‿0⥊⟨⟩ 78 | »0‿9999⥊⟨⟩ %% 0‿9999⥊⟨⟩ 79 | 80 | # TODO: 81 | # ⟨↑‿3⥊↕10 ⋄ ↑‿3⥊<¨↕10 ⋄ ↑‿3⥊<˘↕10⟩ 82 | # 5‿¯5↑⌜⟨↕2 ⋄ "ab" ⋄ <¨↕2 ⋄ <˘↕2⟩ 83 | -------------------------------------------------------------------------------- /src/utils/utf.c: -------------------------------------------------------------------------------- 1 | #include "../core.h" 2 | #include "utf.h" 3 | 4 | static i8 utf8lenb(u8 ch) { 5 | if (ch<128) return 1; 6 | if ((ch>>5)== 0b110) return 2; 7 | if ((ch>>4)== 0b1110) return 3; 8 | if ((ch>>3)==0b11110) return 4; 9 | return -1; 10 | } 11 | static u32 utf8_p(u8* p) { 12 | i32 len = utf8lenb(*p); 13 | switch (len) { default: UD; 14 | case -1: return (u32)-1; 15 | case 1: return *p; 16 | case 2: return (0b11111u&*p)<< 6 | (0b111111u&p[1]); 17 | case 3: return (0b1111u &*p)<<12 | (0b111111u&p[2]) | (0b111111u&p[1])<<6; 18 | case 4: return (0b111u &*p)<<18 | (0b111111u&p[3]) | (0b111111u&p[2])<<6 | (0b111111u&p[1])<<12; 19 | } 20 | } 21 | FORCE_INLINE void utf8_w(char** buf_i, u32 c) { 22 | char* buf = *buf_i; 23 | if (c<128) { *buf++ = c; } 24 | else if (c<=0x07FF) { *buf++ = 0xC0| c>>6 ; *buf++ = 0x80|(c &0x3F); } 25 | else if (c<=0xFFFF) { *buf++ = 0xE0| c>>12; *buf++ = 0x80|(c>>6 &0x3F); *buf++ = 0x80|(c &0x3F); } 26 | else { *buf++ = 0xF0| c>>18; *buf++ = 0x80|(c>>12&0x3F); *buf++ = 0x80|(c>>6&0x3F); *buf++ = 0x80|(c&0x3F); } 27 | *buf_i = buf; 28 | } 29 | 30 | B utf8Decode(const char* s, i64 len) { 31 | u64 sz = 0; 32 | i64 j = 0; 33 | while (true) { 34 | if (j>=len) { 35 | if (j!=len) thrM("Invalid UTF-8"); 36 | break; 37 | } 38 | i8 l = utf8lenb((u8)s[j]); 39 | if (l==-1) thrM("Invalid UTF-8"); 40 | // TODO validate unicode or something 41 | sz++; 42 | j+= l; 43 | } 44 | if (sz==len) { 45 | return m_c8vec((char*)s, len); 46 | } else { 47 | u32* rp; B r = m_c32arrv(&rp, sz); 48 | u64 p = 0; 49 | for (i64 i = 0; i < len; i+= utf8lenb((u8)s[i])) rp[p++] = utf8_p((u8*)s+i); // may read after end, eh 50 | return r; 51 | } 52 | } 53 | 54 | B utf8Decode0(const char* s) { 55 | return utf8Decode(s, strlen(s)); 56 | } 57 | 58 | B utf8DecodeA(I8Arr* a) { // consumes a 59 | B r = utf8Decode((char*)a->a, PIA(a)); 60 | ptr_dec(a); 61 | return r; 62 | } 63 | 64 | 65 | // printing functions should avoid allocations, as they'll be used to print.. the out-of-memory message 66 | #if defined(USE_REPLXX_IO) 67 | void fprintsU32(FILE* f, u32* s, usz len) { 68 | u32* s_e = s+len; 69 | 70 | #define BUF_SZ 1024 71 | char buf[BUF_SZ]; 72 | while (s>6 , 0x80|(c &0x3F) ); 98 | else if (c<=0xFFFF) fprintf(f, "%c%c%c" , 0xE0| c>>12, 0x80|(c>>6 &0x3F), 0x80|(c &0x3F) ); 99 | else fprintf(f, "%c%c%c%c", 0xF0| c>>18, 0x80|(c>>12&0x3F), 0x80|(c>>6&0x3F), 0x80|(c&0x3F)); 100 | } 101 | void fprintsU32(FILE* f, u32* s, usz len) { 102 | for (usz i = 0; i < len; i++) fprintCodepoint(f, s[i]); 103 | } 104 | #endif 105 | 106 | 107 | void fprintsB(FILE* f, B x) { 108 | u8 xe = TI(x,elType); 109 | usz ia = IA(x); 110 | #define BUF_SZ 1024 111 | if (ia==0) return; 112 | if (elChr(xe)) { 113 | if (xe==el_c32) { 114 | fprintsU32(f, c32any_ptr(x), ia); 115 | } else { 116 | u32 buf[BUF_SZ]; 117 | usz i = 0; 118 | while (i < ia) { 119 | usz curr = ia-i; 120 | if (curr>BUF_SZ) curr = BUF_SZ; 121 | COPY_TO(buf, el_c32, 0, x, i, curr); 122 | fprintsU32(f, buf, curr); 123 | i+= curr; 124 | } 125 | } 126 | } else { 127 | SGetU(x) 128 | for (usz i = 0; i < ia; i++) { 129 | B c = GetU(x, i); 130 | if (isC32(c)) fprintCodepoint(f, o2cG(c)); 131 | #if !SEMANTIC_CATCH 132 | else if (c.u==0 || noFill(c)) fprintf(f, " "); 133 | #endif 134 | else thrM("Trying to output non-character"); 135 | } 136 | } 137 | #undef BUF_SZ 138 | } 139 | 140 | u64 utf8lenB(B x) { // doesn't consume; may error as it verifies whether is all chars 141 | assert(isArr(x)); 142 | SGetU(x) 143 | usz ia = IA(x); 144 | u64 res = 0; 145 | for (usz i = 0; i < ia; i++) { 146 | u32 c = o2c(GetU(x,i)); 147 | res+= c<=127? 1 : c<=0x07FF? 2 : c<=0xFFFF? 3 : 4; 148 | } 149 | return res; 150 | } 151 | void toUTF8(B x, char* p) { 152 | SGetU(x) 153 | usz ia = IA(x); 154 | for (usz i = 0; i < ia; i++) utf8_w(&p, o2cG(GetU(x,i))); 155 | } 156 | -------------------------------------------------------------------------------- /src/core/arrFns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ARRV_BPTR_BODY switch (PTY(xa)) { \ 4 | case t_harr: return harrv_ptr(xa); \ 5 | case t_fillarr: return fillarrv_ptr(xa); \ 6 | case t_hslice: return hslicev_ptr(xa); \ 7 | case t_fillslice: return fillslicev_ptr(xa); \ 8 | } 9 | 10 | static B* arrv_bptrG(Arr* xa) { ARRV_BPTR_BODY; UD; } 11 | static B* arr_bptrG(B x) { Arr* xa=a(x); ARRV_BPTR_BODY; UD; } 12 | 13 | static B* arr_bptr(B x) { Arr* xa=a(x); if (HEURISTIC(true)) { ARRV_BPTR_BODY; } return NULL; } 14 | static B* arrv_bptr(Arr* xa) { if (HEURISTIC(true)) { ARRV_BPTR_BODY; } return NULL; } 15 | 16 | static void* tyarrv_ptr(TyArr* x) { 17 | assert(IS_ANY_ARR(PTY(x)) && !IS_TYSLICE(PTY(x))); 18 | return x->a; 19 | } 20 | static void* tyanyv_ptr(Arr* x) { 21 | assert(IS_ANY_ARR(PTY(x))); 22 | return ARR_IS_SLICE(PTY(x))? ((TySlice*)x)->a : ((TyArr*)x)->a; 23 | } 24 | static void* tyslicev_ptr(Arr* x) { 25 | assert(IS_TYSLICE(PTY(x))); 26 | return ((TySlice*)x)->a; 27 | } 28 | 29 | static void* tyarr_ptr(B x) { return tyarrv_ptr(c(TyArr,x)); } 30 | static void* tyany_ptr(B x) { return tyanyv_ptr(a(x)); } 31 | 32 | #define M_TYARR(WM, OVER, MID, RV, PRE) { PRE \ 33 | Arr* r = m_arr((offsetof(TyArr, a) + ( \ 34 | WM==0? ((u64)ia)*w \ 35 | : WM==1? ((u64)ia)<>3) \ 37 | ) OVER, type, ia); \ 38 | MID \ 39 | *rp = RV; \ 40 | return ((TyArr*)r)->a; \ 41 | } 42 | // width in bytes; overalloc is a byte count 43 | SHOULD_INLINE void* m_tyarrp (Arr** rp, usz w, usz ia, u8 type ) M_TYARR(0, , , r, ) 44 | SHOULD_INLINE void* m_tyarrpO(Arr** rp, usz w, usz ia, u8 type, u64 over) M_TYARR(0,+over, , r, ) 45 | SHOULD_INLINE void* m_tyarrv (B* rp, usz w, usz ia, u8 type ) M_TYARR(0, , arr_shVec((Arr*)r);, taga(r), ) 46 | SHOULD_INLINE void* m_tyarrvO(B* rp, usz w, usz ia, u8 type, u64 over) M_TYARR(0,+over, arr_shVec((Arr*)r);, taga(r), ) 47 | SHOULD_INLINE void* m_tyarrc (B* rp, usz w, B x, u8 type ) M_TYARR(0, , arr_shCopy((Arr*)r,x);, taga(r), usz ia = IA(x);) 48 | SHOULD_INLINE void* m_tyarrcO(B* rp, usz w, B x, u8 type, u64 over) M_TYARR(0,+over, arr_shCopy((Arr*)r,x);, taga(r), usz ia = IA(x);) 49 | 50 | // width in log2(bytes) 51 | SHOULD_INLINE void* m_tyarrlp(Arr** rp, usz w, usz ia, u8 type) M_TYARR(1, , , r, ) 52 | SHOULD_INLINE void* m_tyarrlv(B* rp, usz w, usz ia, u8 type) M_TYARR(1, , arr_shVec((Arr*)r);, taga(r), ) 53 | SHOULD_INLINE void* m_tyarrlc(B* rp, usz w, B x, u8 type) M_TYARR(1, , arr_shCopy((Arr*)r,x);, taga(r), usz ia = IA(x);) 54 | 55 | // width in log2(bits) 56 | SHOULD_INLINE void* m_tyarrlbp(Arr** rp, usz w, usz ia, u8 type) M_TYARR(2, , , r, ) 57 | SHOULD_INLINE void* m_tyarrlbv(B* rp, usz w, usz ia, u8 type) M_TYARR(2, , arr_shVec((Arr*)r);, taga(r), ) 58 | SHOULD_INLINE void* m_tyarrlbc(B* rp, usz w, B x, u8 type) M_TYARR(2, , arr_shCopy((Arr*)r,x);, taga(r), usz ia = IA(x);) 59 | 60 | extern INIT_GLOBAL u8 const elType2type[]; 61 | #define el2t(X) elType2type[X] // TODO maybe reorganize array types such that this can just be addition? 62 | extern INIT_GLOBAL u8 const elTypeWidth[]; 63 | #define elWidth(X) elTypeWidth[X] 64 | extern INIT_GLOBAL u8 const elwBitLogT[]; 65 | #define elwBitLog(X) elwBitLogT[X] 66 | extern INIT_GLOBAL u8 const elwByteLogT[]; 67 | #define elwByteLog(X) elwByteLogT[X] 68 | extern INIT_GLOBAL u8 const arrTypeWidthLog[]; 69 | #define arrTypeWidthLog(X) arrTypeWidthLog[X] 70 | extern INIT_GLOBAL u8 const arrTypeBitsLog[]; 71 | #define arrTypeBitsLog(X) arrTypeBitsLog[X] 72 | #define arrNewType(X) el2t(TIi(X,elType)) 73 | 74 | SHOULD_INLINE void arr_check_size(u64 sz, u8 type, u64 ia) { 75 | if (DEBUG) { 76 | assert(IS_ANY_ARR(type) || type==t_harrPartial); 77 | if (!ARR_IS_SLICE(type)) { 78 | if (type==t_harr || type==t_harrPartial) assert(sz >= fsizeof(HArr,a,B,ia)); 79 | else assert(sz >= offsetof(TyArr,a) + (((ia<>3)); 80 | } 81 | } 82 | } 83 | // Log of width in bits: max of 7, and also return 7 if not power of 2 84 | SHOULD_INLINE u8 multWidthLog(usz n, u8 lw) { // Of n elements, 1<>lw); // Max of 7; also handle n==0 87 | } 88 | SHOULD_INLINE u8 kCellWidthLog(B x, ur k) { 89 | assert(isArr(x) && RNK(x)>=1); 90 | u8 lw = arrTypeBitsLog(TY(x)); 91 | ur xr = RNK(x); 92 | if (LIKELY(xr <= k)) return lw; 93 | return multWidthLog(shProd(SH(x), k, xr), lw); 94 | } 95 | SHOULD_INLINE u8 cellWidthLog(B x) { return kCellWidthLog(x, 1); } 96 | 97 | static Arr* m_tyslice(void* data, Arr* parent, u8 type, ux ia) { 98 | assert(IS_ANY_ARR(type) && ARR_IS_SLICE(type)); 99 | Arr* a = m_arr(sizeof(TySlice), type, ia); 100 | ((TySlice*) a)->p = parent; 101 | ((TySlice*) a)->a = data; 102 | return a; 103 | } 104 | -------------------------------------------------------------------------------- /src/utils/valgrind.c: -------------------------------------------------------------------------------- 1 | static void printBitDef(u8 val, u8 def) { 2 | printf("%s", def&1? val&1?"1":"0" : val&1?"¹":"⁰"); 3 | } 4 | 5 | void vg_printDump_p(char* name, void* data, u64 len) { 6 | u8 vbits[len]; 7 | int r = VALGRIND_GET_VBITS(data, vbits, len); 8 | 9 | if(name!=NULL) printf("%s:\n", name); 10 | if (r!=1) printf("(failed to get vbits)\n"); 11 | 12 | for (u64 i = 0; i < len; i++) { 13 | if (i!=0) printf(i&7? " " : "\n"); 14 | u8 cv = ~vbits[i]; 15 | u8 cd = ((u8*)data)[i]; 16 | VALGRIND_SET_VBITS(&cd, &(u8[]){0}, 1); 17 | for (i32 j = 7; j >= 0; j--) { 18 | printBitDef(cd>>j, cv>>j); 19 | } 20 | } 21 | printf("\n"); 22 | } 23 | 24 | void vg_printDefined_u64(char* name, u64 x) { 25 | if(name!=NULL) printf("%s: ", name); 26 | u64 d = vg_getDefined_u64(x); 27 | u64 xv = x; 28 | VALGRIND_MAKE_MEM_DEFINED(&xv, 8); 29 | 30 | for (i32 i = 63; i >= 0; i--) printBitDef(xv>>i, d>>i); 31 | printf("\n"); 32 | } 33 | 34 | u64 vg_rand(u64 x) { // randomize undefined bits in x, and return a value with all bits defined 35 | u64 d = vg_getDefined_u64(x); 36 | if (~d == 0) return x; 37 | return (x & d) | (vgRand64() & ~d); 38 | } 39 | 40 | B vg_validateResult(B x) { 41 | if (!isArr(x)) return x; 42 | void* data; 43 | u64 len; 44 | u8 xe = TI(x,elType); 45 | u64 ia = IA(x); 46 | if (xe!=el_B) { 47 | data = tyany_ptr(x); 48 | if (xe==el_bit) { 49 | i32 left = ia&63; 50 | len = (ia>>6)*8; 51 | if (left) { 52 | u64 last = ((u64*)data)[len/8]; 53 | u64 exp = (1ULL<0) { 98 | if (undefCount>8) fatal("too many unknown bits in index of vg_loadLUT64"); 99 | res = vg_withDefined_u64(res, loadMask(p, unk, res, i & ~unk, 1)); 100 | } 101 | #if DBG_VG_OVERRIDES 102 | vg_printDefined_u64("idx", i); 103 | vg_printDefined_u64("res", res); 104 | #endif 105 | return res; 106 | } 107 | 108 | NOINLINE u64 vg_pext_u64(u64 src, u64 mask) { 109 | u64 maskD = vg_getDefined_u64(mask); 110 | u64 r = vg_undef_u64(0); 111 | i32 ri = 0; 112 | u64 undefMask = 0; 113 | for (i32 i = 0; i < 64; i++) { 114 | u64 c = 1ull<>i)&1) { 136 | r|= (c&1) << i; 137 | c>>= 1; 138 | } 139 | } 140 | #if DBG_VG_OVERRIDES 141 | printf("pdep:\n"); 142 | vg_printDefined_u64("src", src); 143 | vg_printDefined_u64("msk", mask); 144 | vg_printDefined_u64("res", r); 145 | vg_printDefined_u64("exp", _pdep_u64(src, mask)); 146 | #endif 147 | return r; 148 | } 149 | 150 | NOINLINE u64 rand_popc64(u64 x) { 151 | u64 def = vg_getDefined_u64(x); 152 | if (def==~0ULL) return POPC(x); 153 | i32 min = POPC(x & def); 154 | i32 diff = POPC(~def); 155 | i32 res = min + vgRand64Range(diff); 156 | #if DBG_VG_OVERRIDES 157 | printf("popc:\n"); 158 | vg_printDefined_u64("x", x); 159 | printf("popc in %d-%d; res: %d\n", min, min+diff, res); 160 | #endif 161 | return res; 162 | } --------------------------------------------------------------------------------