"elymasAsm.ey" include < { assembler -01 . } ":" deff assembler .|label "@" deff "%" _ : -01 deff < 1 ==PROT_READ 2 ==PROT_WRITE 4 ==PROT_EXEC 2 ==MAP_PRIVATE 16 ==MAP_FIXED 32 ==MAP_ANONYMOUS > ==MMAP < 9 ==mmap > ==SYSCALL 0 ==constantAllocBegin 0 ==constantAllocEnd { =*def ==struct struct values |cat fold ==data constantAllocEnd constantAllocBegin sub ==constantAllocFree data len constantAllocFree gt { data len 1 sub :PAGESIZE udiv 1 add :PAGESIZE mul :alloc ==area area :globalAllocations .register area .base _ constantAllocEnd neq { area .base =constantAllocBegin } rep area .size add =constantAllocEnd } rep sys .asm .|poke =*poke constantAllocBegin _ ==i data { -101 poke 1 add } each =constantAllocBegin struct keys { i -101 def struct -01 . len i add =i } each } /allocateOffsetStruct deff { ==str [ str len :imm64 -- %10 %00 %00 %00 %00 %00 %00 %00 %00 str len :imm64 ] str strToUTF8Bytes cat [ 8 str len 8 umod sub %00 rep ] cat } /toConstString deff [ ] ==stringHoles [ ] ==stringValues { ==str ] _ len ==offset stringValues [ str ] cat =stringValues stringHoles [ { ==allocatedStrings ==opcodes [ allocatedStrings str . :imm64 ] =*bytesToPatch 0 8 range { _ bytesToPatch -01 offset add opcodes =[] } each } ] cat =stringHoles [ -011 len dearray %00 %00 %00 %00 %00 %00 %00 %00 } /string deff { ==opcodes stringValues len 0 gt { < < stringValues { _ toConstString -01 == }' each > { defv }' allocateOffsetStruct > ==allocatedStrings stringHoles { opcodes allocatedStrings -102 * } each [ ] =stringHoles [ ] =stringValues } rep opcodes } /stringResolve deff [ ] ==linkHoles { ==what ==where ] _ len ==offset linkHoles [ { =*resolve [ what resolve :imm64 ] =*bytesToPatch 0 8 range { _ bytesToPatch -01 where resolve offset add add sys .asm .poke } each } ] cat =linkHoles [ -011 len dearray %00 %00 %00 %00 %00 %00 %00 %00 } /linkAbs64 deff { ==resolve linkHoles { resolve -01 * } each [ ] =linkHoles } /linkResolve deff { [ } "[[" deff { ] :labelResolve stringResolve } "]]" deff { %00 %00 %00 %00 %00 %60 %00 %00 } /HEAPBASE deff { %00 %00 %00 %00 %00 %50 %00 %00 } /BLOCKBASE deff { %00 %00 %00 %00 %00 %40 %00 %00 } /MARKBASE deff 4096 16 mul 8 mul ==ALLOCCHUNKSIZE # minimum ALLOCCHUNKSIZE # 4096 16 mul 8 mul 64 mul ==ALLOCCHUNKSIZE # FIXME: there is still some wonkyness with freezing < # current end of heap memory (grows upwards) [ %00 %00 %00 %00 %00 %00 %00 %00 ] ==heapSize # index of next cell likely to be free [ %00 %00 %00 %00 %00 %00 %00 %00 ] ==unusedHeapStart # current parser scope [ %00 %00 %00 %00 %00 %00 %00 %00 ] ==currentScope # current parser quote state [ %00 %00 %00 %00 %00 %00 %00 %00 ] ==currentQuoted > { defv }' allocateOffsetStruct { ==register { _ =*array len _ 4 udiv ==largeMoves 4 umod ==smallMoves 0 ==i largeMoves { i _ 4 add =i _ [ 3 2 1 0 ] add array { -01 256 mul add } fold -01 register :movlImmMemDisp8 } rep i register :addqImm8Reg smallMoves { i _ 1 add =i array register :movbImmMem register :incqReg } rep } } /loadToXXX deff /rdi loadToXXX /loadToRdi deff /rbp loadToXXX /loadToRbp deff { ==register 63 register :btrqImm8Reg [ 8 register register :movqMemDisp8Reg ] len :jcRel8 8 register register :movqMemDisp8Reg } /unboxInteger deff # internal functions, ABI follows SysV standards # except that r8-r15 are also callee-saved < # dump string to stderr for internal error reporting # rdi -> address of string on heap [ 24 /rdi /rsi :leaqMemDisp8Reg 16 /rdi /rdx :movqMemDisp8Reg 2 /rdi :movqImmReg 1 /rax :movqImmReg :syscall :retn ] /internalDumpErrorString defv # rdi -> string to hash # rax <- hash value # This is (or should be) MurmurHash3_x64_128 [[ /rbx :pushqReg /r8 :pushqReg /r9 :pushqReg /r10 :pushqReg /r11 :pushqReg /r12 :pushqReg /r13 :pushqReg # const uint8_t * data = (const uint8_t*)key; 24 /rdi /rsi :leaqMemDisp8Reg # uint64_t h1 = seed; /r12 /r12 :xorqRegReg # uint64_t h2 = seed; /r13 /r13 :xorqRegReg # const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); /r8 :movqImmOOBReg [ %87 %C3 %7B %91 %11 %42 %53 %D5 ] reverse 8 dearray # const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); /r9 :movqImmOOBReg [ %4C %F5 %AD %43 %27 %45 %93 %7F ] reverse 8 dearray # const int nblocks = len / 16; 16 /rdi /rcx :movqMemDisp8Reg 16 /rcx :subqImm8Reg /blockLoopFinished :jbLbl8 # //---------- body # const uint64_t * blocks = (const uint64_t *)(data); # for(int i = 0; i < nblocks; i++) { @blockLoop /rsi /rax :movqMemReg # uint64_t k1 = getblock64(blocks,i*2+0); /r8 :mulqReg # k1 *= c1; 31 /rax :rolqImm8Reg # k1 = ROTL64(k1,31); /r9 :mulqReg # k1 *= c2; /rax /r12 :xorqRegReg # h1 ^= k1; 27 /r12 :rolqImm8Reg # h1 = ROTL64(h1,27); /r13 /r12 :addqRegReg # h1 += h2; %52DCE729 4 /r12 /r12 /r12 :leaqMemIndexScaleDisp32Reg # h1 = h1*5+0x52dce729; 8 /rsi /rax :movqMemDisp8Reg # uint64_t k2 = getblock64(blocks,i*2+1); /r9 :mulqReg # k2 *= c2; 33 /rax :rolqImm8Reg # k2 = ROTL64(k2,33); /r8 :mulqReg # k2 *= c1; /rax /r13 :xorqRegReg # h2 ^= k2; 31 /r13 :rolqImm8Reg # h2 = ROTL64(h2,31); /r12 /r13 :addqRegReg # h2 += h1; %38495AB5 4 /r13 /r13 /r13 :leaqMemIndexScaleDisp32Reg # h2 = h2*5+0x38495ab5; 16 /rsi :addqImm8Reg 16 /rcx :subqImm8Reg /blockLoop :jaeLbl8 # } @blockLoopFinished 16 /rcx :addqImm8Reg /finalize :jzLbl8 # //---------- tail /rcx /rbp :movqRegReg # const uint8_t * tail = (const uint8_t*)(data + nblocks*16); # switch(len & 15) # { # case 8: k1 ^= ((uint64_t)tail[ 7]) << 56; # case 7: k1 ^= ((uint64_t)tail[ 6]) << 48; # case 6: k1 ^= ((uint64_t)tail[ 5]) << 40; # case 5: k1 ^= ((uint64_t)tail[ 4]) << 32; # case 4: k1 ^= ((uint64_t)tail[ 3]) << 24; # case 3: k1 ^= ((uint64_t)tail[ 2]) << 16; # case 2: k1 ^= ((uint64_t)tail[ 1]) << 8; # case 1: k1 ^= ((uint64_t)tail[ 0]) << 0; 1 /rax :movqImmReg 2 /rcx :shlqImm8Reg /rax :shlqClReg /rax :shlqClReg # support 64bit shift /rax :decqReg # rax == bitmask for selection /rsi /rax :andqMemReg /r8 :mulqReg # k1 *= c1; 31 /rax :rolqImm8Reg # k1 = ROTL64(k1,31); /r9 :mulqReg # k1 *= c2; /rax /r12 :xorqRegReg # h1 ^= k1; 8 /rbp :subqImm8Reg /finalize :jbeLbl8 8 /rsi :addqImm8Reg # case 15: k2 ^= ((uint64_t)tail[14]) << 48; # case 14: k2 ^= ((uint64_t)tail[13]) << 40; # case 13: k2 ^= ((uint64_t)tail[12]) << 32; # case 12: k2 ^= ((uint64_t)tail[11]) << 24; # case 11: k2 ^= ((uint64_t)tail[10]) << 16; # case 10: k2 ^= ((uint64_t)tail[ 9]) << 8; # case 9: k2 ^= ((uint64_t)tail[ 8]) << 0; /rbp /rcx :movqRegReg 1 /rax :movqImmReg 2 /rcx :shlqImm8Reg /rax :shlqClReg /rax :shlqClReg /rax :decqReg # rax == bitmask for selection /rsi /rax :andqMemReg /r9 :mulqReg # k2 *= c2; 33 /rax :rolqImm8Reg # k2 = ROTL64(k2,33); /r8 :mulqReg # k2 *= c1; /rax /r13 :xorqRegReg # h2 ^= k2; # }; # //---------- finalization @finalize 16 /rdi /r12 :xorqMemDisp8Reg # h1 ^= len; 16 /rdi /r13 :xorqMemDisp8Reg # h2 ^= len; /r13 /r12 :addqRegReg # h1 += h2; /r12 /r13 :addqRegReg # h2 += h1; %85EBCA6B /r8 :movqImmReg %C2B2AE35 /r9 :movqImmReg # h1 = fmix64(h1); /r12 /rax :movqRegReg 16 /r12 :shrqImm8Reg /r12 /rax :xorqRegReg # h ^= h >> 16; /r8 :mulqReg # h *= 0x85ebca6b; /rax /r12 :movqRegReg 13 /r12 :shrqImm8Reg /r12 /rax :xorqRegReg # h ^= h >> 13; /r9 :mulqReg # h *= 0xc2b2ae35; /rax /r12 :movqRegReg 16 /r12 :shrqImm8Reg /rax /r12 :xorqRegReg # h ^= h >> 16; # h2 = fmix64(h2); /r13 /rax :movqRegReg 16 /r13 :shrqImm8Reg /r13 /rax :xorqRegReg # h ^= h >> 16; /r8 :mulqReg # h *= 0x85ebca6b; /rax /r13 :movqRegReg 13 /r13 :shrqImm8Reg /r13 /rax :xorqRegReg # h ^= h >> 13; /r9 :mulqReg # h *= 0xc2b2ae35; /rax /r13 :movqRegReg 16 /r13 :shrqImm8Reg /rax /r13 :xorqRegReg # h ^= h >> 16; /r13 /r12 :addqRegReg # h1 += h2; /r12 /r13 :addqRegReg # h2 += h1; # ((uint64_t*)out)[0] = h1; /r12 /rax :movqRegReg # ((uint64_t*)out)[1] = h2; /r13 :popqReg /r12 :popqReg /r11 :popqReg /r10 :popqReg /r9 :popqReg /r8 :popqReg /rbx :popqReg :retn ]] /internalHashString defv > { defv }' allocateOffsetStruct { ==str /rdi :movqImmOOBReg str string internalDumpErrorString /rax :movqImmReg /rax :callqReg } /outputError deff < # allocate a chunk of memory # inspiration from http://wiki.luajit.org/new-garbage-collector # rdi -> size of chunk in bytes # rax <- address of allocated chunk # chunk will have GC length header initialized correctly [[ /rbx :pushqReg # /rdi :pushqReg # TODO remove these three lines once load-testing the GC seems unnecessary # /markAndSweep :callqLbl32 # load testing # /rdi :popqReg # load testing /rdi :pushqReg /searchForFreeBlock :callqLbl32 /rax /rax :andqRegReg /success :jnzLbl32 /markAndSweep :callqLbl32 @allocateFromSystemLoop /rdi :popqReg /rdi :pushqReg /searchForFreeBlock :callqLbl32 /rax /rax :andqRegReg /success :jnzLbl32 /allocateFromSystem :callqLbl32 /allocateFromSystemLoop :jmpLbl8 @success /rdi :popqReg /rbx :popqReg :retn # run through block bitmap until sufficiently many zeroes are found # if yes, allocate block @searchForFreeBlock # rdi -> size of chunk in bytes # rax <- address of allocated chunk or zero if no free block was found heapSize /rbp :movqImmReg 0 /rbp /rbp :movqMemDisp8Reg # rbp now holds number of bytes in heap 4 /rbp :shrqImm8Reg # rbp now holds number of 16 byte cells in heap unusedHeapStart /rbx :movqImmReg /rbx /rbx :movqMemReg # /rbx /rbx :xorqRegReg # rbx == index of cell currently tested /r8 :pushqReg /r9 :pushqReg /r8 :movqImmOOBReg BLOCKBASE /r9 :movqImmOOBReg MARKBASE /rbx /rbp :cmpqRegReg /noFreeBlockAvailable :jbeLbl32 /rsi /rsi :xorqRegReg # rsi > 0 => currently counting block extent of free block @searchFreeBlockStart /rbx /r8 :btqRegMem # test block bitmap /nonFreeBlockStartFound :jcLbl8 # block not free /rbx /r9 :btqRegMem # test mark bitmap /freeBlockStartFound :jcLbl8 # block marked, i.e. truly free block start @resumeFreeBlockStartSearch /rsi /rsi :xorqRegReg @nonFreeBlockStartFound /rbx :incqReg /rbx /rbp :cmpqRegReg /searchFreeBlockStart :jaLbl8 /noFreeBlockAvailable :jmpLbl32 @freeBlockStartFound 16 /rsi :addqImm8Reg /rsi /rdi :cmpqRegReg /freeBlockFound :jbeLbl8 /rbx :incqReg /rbx /rbp :cmpqRegReg /noFreeBlockAvailable :jbeLbl32 /rbx /r8 :btqRegMem # test block bitmap /resumeFreeBlockStartSearch :jcLbl8 # block not free /rbx /r9 :btrqRegMem # reset mark bit to reconnect free blocks /freeBlockStartFound :jmpLbl8 @freeBlockFound # rdi == size of block to allocate # rbx == last cell of free block of sufficient size unusedHeapStart /rax :movqImmReg /rbx /rax :movqRegMem # split block if necessary /rbx :incqReg /rbx /rbp :cmpqRegReg /dontSplit :jbeLbl8 /rbx /r8 :btqRegMem # the next block already starts here? /dontSplit :jcLbl8 # yes /rbx /r9 :btsqRegMem # set mark bit if block bit was zero @dontSplit # mark block as used (-> white) 1 neg /rdi /rsi :leaqMemDisp8Reg 4 /rsi :shrqImm8Reg /rsi :incqReg /rsi /rbx :subqRegReg /rbx /r8 :btsqRegMem # set block bit /rbx /r9 :btrqRegMem # reset mark bit # prepare block length and GC header (-> light grey) 59 /rdi :btsqImm8Reg 4 /rbx :shlqImm8Reg /rax :movqImmOOBReg HEAPBASE /rbx /rax :addqRegReg /rdi /rax :movqRegMem /r9 :popqReg /r8 :popqReg :retn @noFreeBlockAvailable /r9 :popqReg /r8 :popqReg /rax /rax :xorqRegReg :retn # run through global state and mark reachable objects @markAndSweep /r8 :pushqReg /r9 :pushqReg /r10 :pushqReg /r11 :pushqReg /r8 :movqImmOOBReg HEAPBASE # constant through mark /r9 :movqImmOOBReg BLOCKBASE # constant through mark /r10 :movqImmOOBReg MARKBASE # constant through mark heapSize /r11 :movqImmReg /r11 /r11 :movqMemReg # constant through mark /r8 /r11 :addqRegReg # r11 == end of heap unusedHeapStart /rax :movqImmReg /rbx /rbx :xorqRegReg /rbx /rax :movqRegMem # zero mark bitmap /r11 /rcx :movqRegReg /r8 /rcx :subqRegReg # rcx now holds number of bytes in heap 10 /rcx :shrqImm8Reg /noResetNecessary :jzLbl8 # rcx now holds number of quadwords in mark bitmap # (16 bytes per cell -> shift by 4 # 8 cells per byte -> shift by 3 # 8 bytes per quadword -> shift by 3) /r9 /rsi :movqRegReg /r10 /rdi :movqRegReg @resetMarkLoop /rsi /rax :movqMemReg /rax :notqReg /rax /rdi :andqRegMem 8 /rsi :addqImm8Reg 8 /rdi :addqImm8Reg /resetMarkLoop :loopLbl8 @noResetNecessary # start from current scope and mark all reachable blocks /r14 /rdi :movqRegReg /markObject :callqLbl32 # start from current coroutine and mark all reachable blocks /r13 /rdi :movqRegReg /markObject :callqLbl32 # start from stack and mark all reachable blocks :STACKBOTTOMMARKER /rcx :movqImmReg /rsp /rsi :movqRegReg /rcx /rsi :cmpqRegMem /dataStackEmpty :jzLbl8 @loopThroughDataStack /rsi /rdi :movqMemReg /markStackObject :callqLbl32 8 /rsi :addqImm8Reg /rcx /rsi :cmpqRegMem /loopThroughDataStack :jnzLbl8 @dataStackEmpty /r15 /rsi :movqRegReg /rcx /rsi :cmpqRegMem /callStackEmpty :jzLbl8 @loopThroughCallStack /rsi /rdi :movqMemReg /markStackObject :callqLbl32 8 /rsi :addqImm8Reg /rcx /rsi :cmpqRegMem /loopThroughCallStack :jnzLbl8 @callStackEmpty # start from encoding buffer and mark all reachable blocks :quoteEncodingBufferObjects /rsi :movqImmReg 0 /rsi :cmpqImm8Mem /quoteEncodingBufferUnused :jzLbl8 :STACKSIZE 8 sub 8 udiv /rcx :movqImmReg @loopThroughEncodingBuffer /rsi /rdi :movqMemReg /markObject :callqLbl32 8 /rsi :addqImm8Reg /loopThroughEncodingBuffer :loopLbl8 @quoteEncodingBufferUnused # free unmarked blocks /r11 /rcx :movqRegReg /r8 /rcx :subqRegReg # rcx now holds number of bytes in heap 10 /rcx :shrqImm8Reg # rcx now holds number of quadwords in mark bitmap # (16 bytes per cell -> shift by 4 # 8 cells per byte -> shift by 3 # 8 bytes per quadword -> shift by 3) /noFreeNecessary :jzLbl8 /r9 /rdi :movqRegReg /r10 /rsi :movqRegReg @freeLoop /rsi /rax :movqMemReg /rdi /rbx :movqMemReg /rax /rdi :andqRegMem /rbx /rsi :orqRegMem # TODO change this to xorqRegMem to auto-clear mark bits 8 /rsi :addqImm8Reg 8 /rdi :addqImm8Reg /freeLoop :loopLbl8 @noFreeNecessary /r11 :popqReg /r10 :popqReg /r9 :popqReg /r8 :popqReg :retn # recursively mark this object reachable # guaranteed not to clobber rcx, rsi (because it is used in many loops) @markStackObject # rdi == address of a reachable object, of reachable code within a code block or some random bits /rdi /r8 :cmpqRegReg /markObjectDone :jaLbl32 # pointing below the heap /rdi /r11 :cmpqRegReg /markObjectDone :jbeLbl32 # pointing above the heap # rdi == address of a reachable object or of reachable code within a code block # scan for object downwards /r8 /rdi :subqRegReg # rdi == byte offset relative to heap begin 4 /rdi :shrqImm8Reg # rdi == cell index of first 16-byte cell of possible object /rbp /rbp :xorqRegReg # rbp == 0: not from stack exploration, function code references ignored if trivial forward /rdi /r9 :btqRegMem # test block bit /markObjectDirectHit :jcLbl8 # direct hit, no need for stack exploration @searchStackObject /rdi :decqReg /rdi /r9 :btqRegMem # test block bit /searchStackObject :jncLbl8 # rdi == cell index of first 16-byte cell of object # TODO optimize this by jumping right into markObject /rdi /r10 :btsqRegMem # set mark bit # but don't test mark bit, because we need to follow function refs even on later passes 4 /rdi :shlqImm8Reg /r8 /rdi :addqRegReg 7 /rdi /al :movbMemDisp8Reg %F0 /al :andbImmReg %B0 /al :cmpbImmReg /markObjectDone :jzLbl8 # stacks not eligible for stack walking 1 /rbp :orqImm8Reg # rbp == 1: here from stack exploration, function code references valid in trivial forward /markObjectUnclean :jmpLbl8 # a short comment is in order to maybe clear up some of the seemingly arbitrary rules of stack walking: # there are multiple reasons why addresses on the stack need to be corrected down to locate objects, # think pointers to arrays, return addresses into function codes, etc. # however, if we'd gladly locate stack objects as well, they'll be stack-walked, and if a stack-pointer # ever ends up on a stack, the GC will stack overflow because stack-located objects are not # already-mark checked. Why so? If the optimizer has replaced a function code with a trivial forward # returning code might still follow references from the unreplaced code. Hence if the code was # located via stack-walking, we need to follow the unreplaced code's references, even if the # function code object was previously marked by some other avenue # FIXME: clean this up, only skip mark check for function code objects actually @markObjectDone :retn # direct hit during stack walking @markObjectDirectHit # rdi == cell index of first 16-byte cell of object /rdi /r10 :btsqRegMem # test mark bit /markObjectDone :jcLbl8 # was already marked 4 /rdi :shlqImm8Reg /r8 /rdi :addqRegReg # rdi == address of reachable object /markObjectUnclean :jmpLbl8 # recursively mark this object reachable # guaranteed not to clobber rcx, rsi (because it is used in many loops) @markObject # rdi == address of a reachable object or some other random bits /rbp /rbp :xorqRegReg # rbp == 0: not from stack exploration, function code references ignored if trivial forward /rdi /r8 :cmpqRegReg /markObjectDone :jaLbl32 # pointing below the heap 15 /dil :testbImmReg /markObjectDone :jnzLbl32 # pointing to unaligned address /rdi /r11 :cmpqRegReg /markObjectDone :jbeLbl32 # pointing above the heap # rdi == address of a reachable object /rdi /rdx :movqRegReg /r8 /rdx :subqRegReg # rdx == byte offset relative to heap begin 4 /rdx :shrqImm8Reg # rdx == cell index of first 16-byte cell of object /rdx /r9 :btqRegMem # test block bit /markObjectDone :jncLbl8 # not pointing to an object /rdx /r10 :btsqRegMem # test mark bit /markObjectDone :jcLbl8 # was already marked @markObjectUnclean # rdi == address of a reachable object /rax /rax :xorqRegReg 7 /rdi /al :movbMemDisp8Reg %F0 /al :andbImmReg 4 /rax :shrqImm8Reg /markInteger :jzLbl32 /rax :decqReg /markString :jzLbl32 /rax :decqReg /markFloat :jzLbl32 2 /rax :subqImm8Reg /markExtensionArea :jzLbl32 /rax :decqReg /markFunction :jzLbl32 /rax :decqReg /markFunctionCode :jzLbl32 /rax :decqReg /markArray :jzLbl32 /rax :decqReg /markFunctionType :jzLbl32 /rax :decqReg /markScope :jzLbl32 /rax :decqReg /markNameTable :jzLbl32 /rax :decqReg /markStack :jzLbl32 /rax :decqReg /markCoroutine :jzLbl32 @markInvalidType /rax /rbx :movqRegReg # for easier inspection "unknown object type during mark phase" outputError :ud2 0 /rax :movqImmReg # dead code for disambiguation in debugging @markInteger # "integer marked\n" outputError :retn 1 /rax :movqImmReg # dead code for disambiguation in debugging @markString # internalDumpErrorString /rax :movqImmReg # /rax :callqReg # " string marked\n" outputError :retn 2 /rax :movqImmReg # dead code for disambiguation in debugging @markFloat # "float marked\n" outputError :retn 4 /rax :movqImmReg # dead code for disambiguation in debugging @markExtensionArea # /rdi :pushqReg # "extension area marked\n" outputError # /rdi :popqReg /rcx :pushqReg /rsi :pushqReg /rdi /ecx :movlMemReg /rdi /rsi :movqRegReg 8 /rcx :subqImm8Reg @markExtensionAreaLoop /rsi /rcx /rdi :movqMemIndexReg /markObject :callqLbl32 8 /rcx :subqImm8Reg /markExtensionAreaLoop :jnzLbl8 /rsi :popqReg /rcx :popqReg :retn 5 /rax :movqImmReg # dead code for disambiguation in debugging @markFunction # /rdi :pushqReg # "function marked\n" outputError # /rdi :popqReg /rdi :pushqReg /rdi :pushqReg 8 /rdi :addqImm8Reg /rdi /rdi :movqMemReg /markObject :callqLbl32 /rdi :popqReg 16 /rdi :addqImm8Reg /rdi /rdi :movqMemReg /markObject :callqLbl32 /rdi :popqReg 24 /rdi :addqImm8Reg /rdi /rdi :movqMemReg /markObject :jmpLbl32 6 /rax :movqImmReg # dead code for disambiguation in debugging @markFunctionCode # /rdi :pushqReg # "function code marked\n" outputError # /rdi :popqReg /rbp /rbp :testqRegReg /markFunctionCodeForward :jzLbl8 # FIXME reenable this one day @markFunctionCodeFull /rcx :pushqReg /rsi :pushqReg /rdi /ecx :movlMemReg 8 /rdi /rcx :subqMemDisp8Reg 8 /rdi /rdi :addqMemDisp8Reg 16 /rcx :subqImm8Reg /markFunctionCodeDone :jzLbl8 16 /rdi /rsi :leaqMemDisp8Reg 3 /rcx :shrqImm8Reg @markFunctionCodeLoop /rsi /rdi :movqMemReg /markObject :callqLbl32 8 /rsi :addqImm8Reg /markFunctionCodeLoop :loopLbl8 @markFunctionCodeDone /rsi :popqReg /rcx :popqReg :retn # function was reached from somewhere but the stack, check if trivial forward code @markFunctionCodeForward 0 [ 0 /rax :movqImmReg ] * 16 /rdi :cmpbImmMemDisp8 /markFunctionCodeFull :jnzLbl8 1 [ 0 /rax :movqImmReg ] * 17 /rdi :cmpbImmMemDisp8 /markFunctionCodeFull :jnzLbl8 0 [ /rax :jmpqReg ] * 26 /rdi :cmpbImmMemDisp8 /markFunctionCodeFull :jnzLbl8 1 [ /rax :jmpqReg ] * 27 /rdi :cmpbImmMemDisp8 /markFunctionCodeFull :jnzLbl8 18 /rdi /rdi :movqMemDisp8Reg 16 /rdi :subqImm8Reg /markObject :jmpLbl32 7 /rax :movqImmReg # dead code for disambiguation in debugging @markArray # /rdi :pushqReg # "array marked\n" outputError # /rdi :popqReg /rcx :pushqReg /rsi :pushqReg /rdi /ecx :movlMemReg /rdi /rsi :movqRegReg 8 /rcx :subqImm8Reg /markArrayEmpty :jzLbl8 @markArrayLoop /rsi /rcx /rdi :movqMemIndexReg /markObject :callqLbl32 8 /rcx :subqImm8Reg /markArrayLoop :jnzLbl8 @markArrayEmpty /rsi :popqReg /rcx :popqReg :retn 8 /rax :movqImmReg # dead code for disambiguation in debugging @markFunctionType # /rdi :pushqReg # "function type marked\n" outputError # /rdi :popqReg /rcx :pushqReg /rsi :pushqReg /rdi /ecx :movlMemReg /rdi /rsi :movqRegReg 8 /rcx :subqImm8Reg @markFunctionTypeLoop /rsi /rcx /rdi :movqMemIndexReg /markObject :callqLbl32 8 /rcx :subqImm8Reg /markFunctionTypeLoop :jnzLbl8 /rsi :popqReg /rcx :popqReg :retn 9 /rax :movqImmReg # dead code for disambiguation in debugging @markScope # /rdi :pushqReg # "scope marked\n" outputError # /rdi :popqReg /rcx :pushqReg /rsi :pushqReg /rdi /ecx :movlMemReg /rdi /rsi :movqRegReg 8 /rcx :subqImm8Reg @markScopeLoop /rsi /rcx /rdi :movqMemIndexReg /markObject :callqLbl32 8 /rcx :subqImm8Reg /markScopeLoop :jnzLbl8 /rsi :popqReg /rcx :popqReg :retn 10 /rax :movqImmReg # dead code for disambiguation in debugging @markNameTable # /rdi :pushqReg # "name table marked\n" outputError # /rdi :popqReg /rcx :pushqReg /rsi :pushqReg /rdi /ecx :movlMemReg /rdi /rsi :movqRegReg 16 /rcx :subqImm8Reg /markNameTableEmpty :jzLbl8 @markNameTableLoop /rsi /rcx /rdi :movqMemIndexReg /markObject :callqLbl32 16 /rcx :subqImm8Reg /markNameTableLoop :jnzLbl8 @markNameTableEmpty /rsi :popqReg /rcx :popqReg :retn 11 /rax :movqImmReg # dead code for disambiguation in debugging @markStack # /rdi :pushqReg # "stack marked\n" outputError # /rdi :popqReg /rsi :pushqReg /rcx :pushqReg :STACKBOTTOMMARKER /rcx :movqImmReg 8 /rdi /rsi :movqMemDisp8Reg /rcx /rsi :cmpqRegMem /stackEmpty :jzLbl8 @loopThroughStack /rsi /rdi :movqMemReg /markStackObject :callqLbl32 8 /rsi :addqImm8Reg /rcx /rsi :cmpqRegMem /loopThroughStack :jnzLbl8 @stackEmpty /rcx :popqReg /rsi :popqReg :retn 12 /rax :movqImmReg # dead code for disambiguation in debugging @markCoroutine # /rdi :pushqReg # "coroutine marked\n" outputError # /rdi :popqReg /rsi :pushqReg /rdi /rsi :movqRegReg 8 /rsi /rdi :movqMemDisp8Reg /markStackObject :callqLbl32 16 /rsi /rdi :movqMemDisp8Reg /markObject :callqLbl32 24 /rsi /rdi :movqMemDisp8Reg /markObject :callqLbl32 32 /rsi /rdi :movqMemDisp8Reg /rsi :popqReg /markObject :jmpLbl32 # allocate next chunk of memory from the operating system @allocateFromSystem heapSize /rax :movqImmReg /rax /rdi :movqMemReg ALLOCCHUNKSIZE /rax :movqImmReg # minimum size of new block /rdi /rcx :movqRegReg 24 /rcx :shrqImm8Reg /rcx :incqReg /rdx /rdx :xorqRegReg /rcx :mulqReg # allocate more if heap is already large /rax /rsi :movqRegReg /rsi :pushqReg /rsi /rdi :addqRegReg heapSize /rax :movqImmReg /rdi /rax :movqRegMem /rsi /rdi :subqRegReg /rax :movqImmOOBReg HEAPBASE /rax /rdi :addqRegReg /mmapBlock :callqLbl32 # also allocate block and mark bitmaps heapSize /rax :movqImmReg /rax /rdi :movqMemReg 7 /rdi :shrqImm8Reg /rsi :popqReg 7 /rsi :shrqImm8Reg /rsi :pushqReg /rsi /rdi :subqRegReg /rax :movqImmOOBReg BLOCKBASE /rax /rdi :addqRegReg /mmapBlock :callqLbl32 heapSize /rax :movqImmReg /rax /rdi :movqMemReg 7 /rdi :shrqImm8Reg /rsi :popqReg /rsi /rdi :subqRegReg /rax :movqImmOOBReg MARKBASE /rax /rdi :addqRegReg /rdi :pushqReg /mmapBlock :callqLbl32 /rdi :popqReg %01 /rdi :movbImmMem # mark whole block free :retn @mmapBlock # rdi == target address # rsi == size in bytes # record new block in global allocation list :globalAllocations .base /rax :movqImmReg 16 /rax :addqImm8Mem /rax /rax :addqMemReg 16 /rax :subqImm8Reg /rdi /rax :movqRegMem /rsi 8 /rax :movqRegMemDisp8 SYSCALL .mmap /rax :movqImmReg # /rdi already fine # /rsi already fine < { MMAP -01 . } "!" deff !PROT_READ !PROT_WRITE !PROT_EXEC bor bor /rdx :movqImmReg !MAP_PRIVATE !MAP_FIXED !MAP_ANONYMOUS bor bor /r10 :movqImmReg > -- /r8 :movqImmOOBReg %FF %FF %FF %FF %FF %FF %FF %FF 0 /r9 :movqImmReg :syscall # TODO error handling :retn ]] /internalAllocate defv > { defv }' allocateOffsetStruct < # rdi -> first string # rsi -> second string # rax <- 1 if both strings are equal, 0 otherwise [[ # compare lengths 16 /rdi /rax :movqMemDisp8Reg 16 /rsi /rax :cmpqMemDisp8Reg /different :jnzLbl8 # compute hashes 0 8 /rdi :cmpqImm8MemDisp8 /firstStringHashed :jnzLbl8 /rdi :pushqReg /rsi :pushqReg internalHashString /rax :movqImmReg /rax :callqReg /rsi :popqReg /rdi :popqReg @firstStringHashed 0 8 /rsi :cmpqImm8MemDisp8 /secondStringHashed :jnzLbl8 /rdi :pushqReg /rsi :pushqReg /rsi /rdi :movqRegReg internalHashString /rax :movqImmReg /rax :callqReg /rsi :popqReg /rdi :popqReg @secondStringHashed # compare hashes 8 /rdi /rax :movqMemDisp8Reg 8 /rsi /rax :cmpqMemDisp8Reg /different :jnzLbl8 # compare contents 16 /rdi /rcx :movqMemDisp8Reg 24 /rdi /rdi :leaqMemDisp8Reg 24 /rsi /rsi :leaqMemDisp8Reg :repz :cmpsb /different :jneLbl8 1 /rax :movqImmReg :retn @different /rax /rax :xorqRegReg :retn ]] /internalCompareString defv > { defv }' allocateOffsetStruct < # allocate a chunk of memory and zero it # rdi -> size of chunk in bytes # rax <- address of allocated chunk # chunk will have GC length header initialized correctly [[ internalAllocate /rax :movqImmReg /rax :callqReg /rax /ecx :movlMemReg 3 /rcx :shrqImm8Reg 1 /rcx :subqImm8Reg 8 /rax /rdi :leaqMemDisp8Reg /rsi /rax :xchgqRegReg /rax /rax :xorqRegReg :reprcx :stosq /rsi /rax :xchgqRegReg :retn ]] /internalAllocateAndZero defv # resolve element from scope # rdi -> address of scope on the heap # rsi -> address of element name on the heap # rax <- address of element on the heap (0 if nonexistant) # rdx <- %xy # y 0 eq if element is passive # y 1 eq if element is active # y 2 eq if element is quote-active # x 1 band if element is static # x 2 band if element is type constant # x 4 band if element is constant # x 8 band if element is deep constant # rcx <- address of entry (i.e. where rax was loaded from) # rdi <- number of parent pointers followed # rsi <- entry index * 8 within scope # rbp <- 0 if within scope data area # 1 if within extension area [[ /rax /rax :xorqRegReg /rax :pushqReg /rdi :pushqReg /rsi :pushqReg @retryWithParent # CHECK this is just sanity checking 7 /rdi /al :movbMemDisp8Reg %F0 /al :andbImmReg %90 /al :cmpbImmReg /isScope :jeLbl8 /rdi :pushqReg :ERRORMARKER /rax :movqImmReg /rax :pushqReg "object resolving in is not a scope" outputError :ud2 @isScope # ENDCHECK 8 /rsi /rcx :movqMemDisp8Reg /rcx /rcx :testqRegReg /hashComputed :jnzLbl8 /rsi /rdi :movqRegReg internalHashString /rax :movqImmReg /rax :callqReg /rax /rcx :movqRegReg 0 /rsp /rsi :movqMemDisp8Reg /rax 8 /rsi :movqRegMemDisp8 # cache hash value in string 8 /rsp /rdi :movqMemDisp8Reg @hashComputed 8 /rdi /rbp :movqMemDisp8Reg # load name table /rbp /rbp :testqRegReg /end :jzLbl32 # no name table present - empty scope < { # rcx == hash value of key to resolve # rbp == nametable to resolve in # 0 /rsp == key to find /ecx /eax :movlRegReg 0 /rbp /esi :movlMemDisp8Reg # load table length 16 /rsi :subqImm8Reg # substract header length 4 /rsi :shrqImm8Reg # divide by slot length /rdx /rdx :xorqRegReg /rsi :divqReg # rdx == remainder, i.e. slot index 4 /rdx :shlqImm8Reg # multiply by slot length 16 1 /rbp /rdx /rax :leaqMemIndexScaleDisp8Reg # load key from bucket 0 /rax :cmpqImm8Mem /end :jzLbl32 # empty slot found, key is not present 0 /rsp /rdi :movqMemDisp8Reg /rax /rsi :movqMemReg /rax :pushqReg /rcx :pushqReg /rbp :pushqReg internalCompareString /rax :movqImmReg /rax :callqReg /rbp :popqReg /rcx :popqReg /rax /rax :testqRegReg /found :jnzLbl32 /rax :popqReg } /trySlot deff :HASHPOSITIONS 1 sub { trySlot /rcx /rax :movqRegReg 32 /rax :shrqImm8Reg /rax /rcx :addqRegReg # derive a new hash value } rep trySlot > -- @end # not found at all, retry with parent /rsi :popqReg /rdi :popqReg /rax :popqReg /rax :incqReg 16 /rdi /rdi :movqMemDisp8Reg /rdi /rdi :testqRegReg /rax :pushqReg /rdi :pushqReg /rsi :pushqReg /retryWithParent :jnzLbl32 @failed /rsi :popqReg /rdi :popqReg /rax :popqReg /rax /rax :xorqRegReg /rdx /rdx :xorqRegReg :retn @found /rdx :popqReg # rdx == hash bucket where we found the key /rsi :popqReg # rsi == name to resolve /rdi :popqReg # rdi == scope we are resolving in # 0 /rsp -> number of parent pointers followed 8 /rdx /eax :movlMemDisp8Reg # load default activation 12 /rdx /ecx :movlMemDisp8Reg # load index in scope 3 /rcx :shlqImm8Reg # rcx == entry index * 8 in scope # rax == entry default activation /rcx /rsi :movqRegReg # save into target register for return value /rbp /rbp :xorqRegReg 32 /rcx :addqImm8Reg # add scope header size /ecx /rdi :cmplRegMem # TODO this fails for > 4 GB scopes /inDataArea :jaLbl8 /rdi /ecx :sublMemReg # substract scope length 24 /rdi /rdi :movqMemDisp8Reg # load extension area pointer /rdi /rdi :testqRegReg /outsideExtensionArea :jzLbl8 /rbp :incqReg 8 /rcx :addqImm8Reg # add extension area header length @inDataArea /rcx /rdi /rdx :movqMemIndexReg # load entry pointer /rax /rdx :xchgqRegReg /rdi /rcx :addqRegReg /rdi :popqReg :retn @outsideExtensionArea /rax :popqReg /rax /rax :xorqRegReg :retn ]] /internalResolve defv # return a pointer to start of object # rdi -> pointer somewhere into an object (or outside of heap) # rax <- pointer to start of object, zero if outside of heap [[ /rax :movqImmOOBReg HEAPBASE /rdx :movqImmOOBReg BLOCKBASE /rax /rdi :subqRegReg /nonHeapObject :jbLbl8 4 /rdi :shrqImm8Reg # rdi == cell index of pointer target /testForStart :jmpLbl8 @scanLoop /rdi :decqReg @testForStart /rdi /rdx :btqRegMem /scanLoop :jncLbl8 4 /rdi :shlqImm8Reg /rdi /rax :addqRegReg :retn @nonHeapObject /rax /rax :xorqRegReg :retn ]] /internalObjectStart defv > { defv }' allocateOffsetStruct # TODO: link internal functions statically with relative calls < # allocate int # rax <- address of allocated integer # chunk will have GC length header initialized correctly [[ heapSize /rbp :movqImmReg unusedHeapStart /rcx :movqImmReg /rdx :movqImmOOBReg HEAPBASE 0 /rbp /rbp :movqMemDisp8Reg # rbp now holds number of bytes in heap /rcx /rcx :movqMemReg /rdi :movqImmOOBReg BLOCKBASE /rsi :movqImmOOBReg MARKBASE 6 /rcx :shrqImm8Reg # extract quadword 10 /rbp :shrqImm8Reg # rbp now holds number of quad-words in bitmaps @testBlockBitLoop /rcx /rbp :cmpqRegReg /noFreeBlockAvailable :jbeLbl32 8 /rcx /rdi /rax :movqMemIndexScaleReg /rax :notqReg 8 /rcx /rsi /rax :andqMemIndexScaleReg /rax /rax :bsfqRegReg # find bit with !block & mark -> free /continueTestBlockBitLoop :jzLbl8 /rax 8 /rcx /rdi :btsqRegMemIndexScale # set block bit of new block /rax 8 /rcx /rsi :btrqRegMemIndexScale # reset mark bit of new block # split block if necessary /rax :incqReg 6 /rax :btqImm8Reg 0 /rcx :adcqImm8Reg %3F /rax :andqImm8Reg /rcx /rbp :cmpqRegReg /dontSplit :jbeLbl8 # next cell outside of heap /rax 8 /rcx /rdi :btqRegMemIndexScale /dontSplit :jcLbl8 # next cell already allocated /rax 8 /rcx /rsi :btsqRegMemIndexScale # set mark bit @dontSplit 6 /rcx :shlqImm8Reg /rax /rcx :addqRegReg # rcx == cell index of cell after allocated int unusedHeapStart /rax :movqImmReg /rcx /rax :movqRegMem /rcx :decqReg # rcx == cell index of allocated int 4 /rcx :shlqImm8Reg # rcx == offset of allocated int /rdx /rcx /rax :leaqMemIndexReg /rcx :movqImmOOBReg %10 %00 %00 %00 %00 %00 %00 %08 # -> light grep /rcx /rax :movqRegMem # initialize GC header :retn @continueTestBlockBitLoop /rcx :incqReg /testBlockBitLoop :jmpLbl8 @noFreeBlockAvailable 10 /rdi :movqImmReg internalAllocate /rax :movqImmReg /rax :jmpqReg ]] /internalAllocateInteger defv # allocate scope, expecting rdi entries # rdi -> expected number of entries # rsi -> parent scope # rax <- address of scope on the heap [ /rsi :pushqReg 3 /rdi :shlqImm8Reg 32 /rdi :addqImm8Reg internalAllocateAndZero /rax :movqImmReg /rax :callqReg # set type and existence of all pointers %96 7 /rax :orbImmMemDisp8 16 /rax :popqMemDisp8 # set parent :retn ] /internalAllocateScope defv # allocate nametable # rdi -> expected number of entries # rax <- address of nametable on the heap [ 4 /rdi :shlqImm8Reg 32 /rdi :addqImm8Reg internalAllocateAndZero /rax :movqImmReg /rax :callqReg # set type %A0 7 /rax :orbImmMemDisp8 :retn ] /internalAllocateNametable defv # insert string into nametable # rdi -> string to insert # rsi -> nametable to insert within # rax <- address of 16 byte hash slot, or zero if no free bucket was found # rdx <- zero if new entry, one if same key was already present [[ /rdi :pushqReg /rsi :pushqReg 58 /rsi :btqImm8Mem /noSlotFound :jcLbl32 # nametable has template bit set, cannot modify 8 /rdi /rcx :movqMemDisp8Reg # load cached hash value /rcx /rcx :testqRegReg /hashComputed :jnzLbl8 internalHashString /rax :movqImmReg /rax :callqReg /rax /rcx :movqRegReg 8 /rsp /rdi :movqMemDisp8Reg /rax 8 /rdi :movqRegMemDisp8 # cache hash value in string 0 /rsp /rsi :movqMemDisp8Reg @hashComputed < { # rcx == hash value # rsi == nametable to insert into # rdi == string to insert /ecx /eax :movlRegReg /rsi /esi :movlMemReg # load table length 16 /rsi :subqImm8Reg # substract header length 4 /rsi :shrqImm8Reg # divide by slot length /rdx /rdx :xorqRegReg /rsi :divqReg # rdx == remainder of division 0 /rsp /rsi :movqMemDisp8Reg 4 /rdx :shlqImm8Reg # multiply by slot length 16 1 /rdx /rsi /rax :leaqMemIndexScaleDisp8Reg # rax == slot address 0 /rax :cmpqImm8Mem /emptySlotFound :jzLbl32 /rax /rsi :movqMemReg /rax :pushqReg /rcx :pushqReg internalCompareString /rax :movqImmReg /rax :callqReg /rcx :popqReg /rax /rax :testqRegReg /equalSlotFound :jnzLbl32 /rax :popqReg 8 /rsp /rdi :movqMemDisp8Reg 0 /rsp /rsi :movqMemDisp8Reg } /trySlot deff :HASHPOSITIONS 1 sub { trySlot /rcx /rax :movqRegReg 32 /rax :shrqImm8Reg /rax /rcx :addqRegReg # derive a new hash value } rep trySlot > -- @noSlotFound /rax /rax :xorqRegReg # no slot found /rdx /rdx :xorqRegReg 16 /rsp :addqImm8Reg :retn @emptySlotFound # rax == slot address /rsi :popqReg /rax :popqMem # save string into slot /rdx /rdx :xorqRegReg :retn @equalSlotFound /rax :popqReg 16 /rsp :addqImm8Reg 1 /rdx :movqImmReg :retn ]] /internalInsertToNametable defv # allocate function # rdi -> code pointer # rsi -> scope pointer # rdx -> type pointer # rax <- address of function on the heap [ /rdi :pushqReg /rdx :pushqReg /rsi :pushqReg 32 /rdi :movqImmReg internalAllocate /rax :movqImmReg /rax :callqReg # set type %50 7 /rax :orbImmMemDisp8 /rsi :popqReg /rsi 8 /rax :movqRegMemDisp8 /rdx :popqReg /rdx 16 /rax :movqRegMemDisp8 /rdi :popqReg /rdi 24 /rax :movqRegMemDisp8 :retn ] /internalAllocateFunction defv # allocate code block # rdi -> number of code bytes # rax <- address of code block on heap [ 16 /rdi :addqImm8Reg internalAllocate /rax :movqImmReg /rax :callqReg # set type %60 7 /rax :orbImmMemDisp8 :retn ] /internalAllocateCode defv # allocate array, expecting rdi/8 entries # rdi -> expected number of entry bytes # rax <- address of array on the heap [ 8 /rdi :addqImm8Reg internalAllocate /rax :movqImmReg /rax :callqReg # set type %70 7 /rax :orbImmMemDisp8 :retn ] /internalAllocateArray defv # allocate string, holding rdi bytes # rdi -> expected number of bytes # rax <- address of string on the heap [ /rdi :pushqReg /rdi :decqReg 3 /rdi :shrqImm8Reg 4 /rdi :addqImm8Reg 3 /rdi :shlqImm8Reg internalAllocate /rax :movqImmReg /rax :callqReg # set type %10 7 /rax :orbImmMemDisp8 0 8 /rax :andqImm8MemDisp8 16 /rax :popqMemDisp8 :retn ] /internalAllocateString defv # allocate empty stack # rax <- new empty stack [ :STACKSIZE /rdi :movqImmReg internalAllocate /rax :movqImmReg /rax :callqReg # set type %B0 7 /rax :orbImmMemDisp8 :STACKSIZE /rax /rdi :leaqMemDisp32Reg 8 /rdi :subqImm8Reg :STACKBOTTOMMARKER /rdx :movqImmReg /rdx /rdi :movqRegMem /rdi 8 /rax :movqRegMemDisp8 :retn ] /internalAllocateStack defv # allocate coroutine state # rdi -> instruction pointer # rsi -> scope pointer # rax <- new coroutine state # does not do any stack copying [[ /rsi :pushqReg /rdi :pushqReg 40 /rdi :movqImmReg internalAllocate /rax :movqImmReg /rax :callqReg # set type %C0 7 /rax :orbImmMemDisp8 8 /rax :popqMemDisp8 16 /rax :popqMemDisp8 0 24 /rax :andqImm8MemDisp8 0 32 /rax :andqImm8MemDisp8 :retn ]] /internalAllocateCoroutine defv > { defv }' allocateOffsetStruct < # allocate float # rax <- address of float object on heap [ internalAllocateInteger /rax :movqImmReg /rax :callqReg # set type %20 7 /rax :orbImmMemDisp8 :retn ] /internalAllocateFloat defv # allocate a new code block from the encoding buffers # rdi -> end of allocated code (address after last used opcode in quoteEncodingBufferCode) # rbp -> end of allocated object pointers (address after last used address in quoteEncodingBufferObjects) # rax <- resulting code object [[ :quoteEncodingBufferCode /rax :movqImmReg /rax /rdi :subqRegReg /rdi :decqReg 3 /rdi :shrqImm8Reg /rdi :incqReg 3 /rdi :shlqImm8Reg :quoteEncodingBufferObjects /rax :movqImmReg /rax /rbp :subqRegReg /rbp /rbp :andqRegReg /atLeastOnePointer :jnzLbl8 /rbp /rax :movqRegMem # store fake zero pointer 8 /rbp :addqImm8Reg @atLeastOnePointer /rbp :pushqReg # store pointer byte count /rdi :pushqReg # store opcode byte count (rounded up to 8 byte) /rbp /rdi :addqRegReg internalAllocateCode /rax :movqImmReg /rax :callqReg # rax == code block on heap # copy opcodes :quoteEncodingBufferCode /rsi :movqImmReg 16 /rax /rdi :leaqMemDisp8Reg /rcx :popqReg /rcx 8 /rax :movqRegMemDisp8 :reprcx :movsb # copy object pointers :quoteEncodingBufferObjects /rsi :movqImmReg /rcx :popqReg :reprcx :movsb # mark buffer unused :quoteEncodingBufferObjects /rdi :movqImmReg /rcx /rdi :movqRegMem # rcx is conveniently zero :retn ]] /internalAllocateCodeFromEncodingBuffer defv # rdi -> nametable to enlarge # rsi -> number of entries to have in the new hashtable # rax <- new, larger nametable [[ /rdi :pushqReg @retry /rsi /rdi :movqRegReg internalAllocateNametable /rax :movqImmReg /rax :callqReg /rax :pushqReg 8 /rsp /rsi :movqMemDisp8Reg 8 /rsi /rdx :movqMemDisp8Reg /rdx 8 /rax :movqRegMemDisp8 # transfer entry count /rsi /ecx :movlMemReg 16 /rcx :subqImm8Reg # substract header size 4 /rcx :shrqImm8Reg # divide by 16 to get number of buckets # rcx == number of entries to rehash @rehashEntry 16 /rsi :addqImm8Reg # rsi == hash bucket to rehash 0 /rsi :cmpqImm8Mem /entryIsEmpty :jzLbl8 /rcx :pushqReg /rsi :pushqReg /rsi /rdi :movqMemReg 16 /rsp /rsi :movqMemDisp8Reg internalInsertToNametable /rax :movqImmReg /rax :callqReg /rax /rax :testqRegReg /rehashFailed :jzLbl8 /rsi :popqReg /rcx :popqReg 8 /rsi /rdx :movqMemDisp8Reg /rdx 8 /rax :movqRegMemDisp8 # transfer entry data @entryIsEmpty /rehashEntry :loopLbl8 /rax :popqReg 8 /rsp :addqImm8Reg :retn @rehashFailed 16 /rsp :addqImm8Reg /rax :popqReg /rax /esi :movlMemReg 3 /rsi :shrqImm8Reg /retry :jmpLbl8 ]] /internalGrowNametable defv > { defv }' allocateOffsetStruct [ 8 /r15 :subqImm8Reg /r15 :popqMem 8 /r15 :subqImm8Reg /r14 /r15 :movqRegMem /r14 /rsi :movqRegReg 2 /rdi :movqImmReg # FIXME: this should use INITIALSCOPESIZE internalAllocateScope /rax :movqImmReg /rax :callqReg /rax /r14 :movqRegReg ] /scopingFunctionHeader defv [ /r15 /r14 :movqMemReg 8 /r15 :addqImm8Reg /r15 :pushqMem 8 /r15 :addqImm8Reg :retn ] /scopingFunctionFooter defv [ 8 /r15 :subqImm8Reg /r15 :popqMem ] /unscopingFunctionHeader defv [ /r15 :pushqMem 8 /r15 :addqImm8Reg :retn ] /unscopingFunctionFooter defv { strToUTF8Bytes _ =*v len _ ==exactLength 1 sub 8 udiv 4 add 8 mul ==memoryLength memoryLength 2147483648 lt not { "constant string too long" die } rep [ # allocate string memoryLength /rdi :movqImmReg internalAllocate /rax :movqImmReg /rax :callqReg # push string address on program stack /rax :pushqReg # set type 7 /rax :addqImm8Reg %10 /rax :orbImmMem # clear hash value 1 /rax :addqImm8Reg /rdx /rdx :xorqRegReg /rdx /rax :movqRegMem # load exact length 8 /rax :addqImm8Reg exactLength /rdx :movqImmReg /rdx /rax :movqRegMem exactLength 0 neq { # load string contents 0 exactLength 1 sub 8 udiv 1 add range { 8 mul ==i 8 /rax :addqImm8Reg /rdx :movqImmOOBReg i _ 8 add range v 8 dearray /rdx /rax :movqRegMem } each } rep ] } /constStringCode deff > /assemblerLibrary defv # vim: syn=elymas