< < # read absolute address # 0 -> address to read # 0 <- byte read [[ /rbx :popqReg /rcx :popqReg /rcx ::unboxInteger /rcx /rcx :movzxMem8Reg64 63 /rcx :btsqImm8Reg /rcx :pushqReg /rbx :pushqReg :retn ]] /eypeek defv # write absolute address # 0 -> address to write # 1 -> value to write [[ /rbx :popqReg /rax :popqReg /rax ::unboxInteger /rcx :popqReg /rcx ::unboxInteger /cl /rax :movbRegMem /rbx :pushqReg :retn ]] /eypoke defv # call absolute address # 0 -> address to call [[ /rbx :popqReg /rax :popqReg /rax ::unboxInteger /rax :callqReg /rbx :pushqReg :retn ]] /eyexecute defv # conduct a syscall # 0 -> syscall number, rax before entry # 1 -> r9 before entry # 2 -> r8 before entry # 3 -> r10 before entry # 4 -> rdx before entry # 5 -> rsi before entry # 6 -> rdi before entry # 0 <- rdx after syscall # 1 <- rax after syscall [[ 8 /r15 :subqImm8Reg /r15 :popqMem 2 { ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /r15 :subqImm8Reg /rax /r15 :movqRegMem } rep # allocate return integers [ /rax /r9 /r8 /r10 /rdx /rsi /rdi ] { ==reg reg :popqReg 63 reg :btrqImm8Reg /intLoadUnboxed reg cat :jcLbl8 7 reg /bl :movbMemDisp8Reg %F0 /bl :andbImmReg /intLoadBoxed reg cat :jzLbl8 %10 /bl :cmpbImmReg /stringLoad reg cat :jeLbl8 "neither int nor string argument in sys .asm .syscall" ::outputError :ud2 /stringLoad reg cat :label 24 reg reg :leaqMemDisp8Reg /doneLoad reg cat :jmpLbl8 /intLoadBoxed reg cat :label 8 reg reg :movqMemDisp8Reg /intLoadUnboxed reg cat :label /doneLoad reg cat :label } each :syscall /r15 /rcx :movqMemReg /rax 8 /rcx :movqRegMemDisp8 /rcx :pushqReg 8 /r15 :addqImm8Reg /r15 /rcx :movqMemReg /rdx 8 /rcx :movqRegMemDisp8 /rcx :pushqReg 8 /r15 :addqImm8Reg /r15 :pushqMem 8 /r15 :addqImm8Reg :retn ]] /eysyscall defv # returns the number of allocations in the global allocation list # 0 <- number of allocations registered [[ /rbx :popqReg :globalAllocations .base /rdx :movqImmReg /rdx /rdx :movqMemReg 4 /rdx :shrqImm8Reg /rdx :decqReg 63 /rdx :btsqImm8Reg /rdx :pushqReg /rbx :pushqReg :retn ]] /eyglobalAllocCount defv # returns the base address of a global allocation # 0 -> number of allocation # 0 <- allocation base address [[ /rbx :popqReg # allocate return integer ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rcx :popqReg /rax :pushqReg /rcx ::unboxInteger :globalAllocations .base /rdx :movqImmReg /rcx :incqReg 4 /rcx :shlqImm8Reg /rcx /rdx /rdx :movqMemIndexReg /rdx 8 /rax :movqRegMemDisp8 /rbx :pushqReg :retn ]] /eyglobalAllocBase defv # returns the size of a global allocation # 0 -> number of allocation # 0 <- allocation size [[ /rbx :popqReg # allocate return integer ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rcx :popqReg /rax :pushqReg /rcx ::unboxInteger :globalAllocations .base /rdx :movqImmReg /rcx :incqReg 4 /rcx :shlqImm8Reg 8 1 /rcx /rdx /rdx :movqMemIndexScaleDisp8Reg /rdx 8 /rax :movqRegMemDisp8 /rbx :pushqReg :retn ]] /eyglobalAllocSize defv # get number of unused cells until top of data stack # 0 <- number of remaining stack cells [[ /rbx :popqReg /rcx /rcx :xorqRegReg /rcx :decqReg :STACKTOPMARKER /rax :movqImmReg /rsp /rdi :movqRegReg :std :repnz :scasq :cld /rcx :negqReg 63 /rcx :btsqImm8Reg /rcx :pushqReg /rbx :pushqReg :retn ]] /eyremainingDataStack defv # get number of unused cells until top of call stack # 0 <- number of remaining stack cells [[ /rbx :popqReg /rcx /rcx :xorqRegReg /rcx :decqReg :STACKTOPMARKER /rax :movqImmReg /r15 /rdi :movqRegReg :std :repnz :scasq :cld /rcx :negqReg 63 /rcx :btsqImm8Reg /rcx :pushqReg /rbx :pushqReg :retn ]] /eyremainingCallStack defv # get raw object address from object # 0 -> object # 0 <- address of the object [[ /rbx :popqReg # allocate return integer ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg /rbx :pushqReg :retn ]] /eyrawAddress defv # generate object from raw object # 0 -> address of the object # 0 <- object [[ /rbx :popqReg /rax :popqReg /rax ::unboxInteger /rax :pushqReg # push integer value /rbx :pushqReg :retn ]] /eyrawObject defv # get raw code execution address from function object # 0 -> function object # 0 <- address of first instruction [[ /rbx :popqReg # allocate return integer ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rdx :popqReg 24 /rdx /rdx :movqMemDisp8Reg 16 /rdx :addqImm8Reg /rdx 8 /rax :movqRegMemDisp8 /rax :pushqReg /rbx :pushqReg :retn ]] /eyrawCodeAddress defv # replace code block by other code # 0 -> code block to replate (and patch) # 1 -> new opcodes as integer array # 2 -> new references as array [[ 32 /r15 :subqImm8Reg 24 /r15 :popqMemDisp8 # store return address 16 /r15 :popqMemDisp8 # code block to patch /r15 :popqMem # new references source 8 /r15 :popqMemDisp8 # new opcode source # copy new opcodes :quoteEncodingBufferCode /rdi :movqImmReg 8 /r15 /rsi :movqMemDisp8Reg /rsi /ecx :movlMemReg 8 /rsi :addqImm8Reg 3 /rcx :shrqImm8Reg /rcx :decqReg /noOpcodesToCopy :jzLbl8 @opcodeCopyLoop /rsi /rax :movqMemReg /rax ::unboxInteger :stosb 8 /rsi :addqImm8Reg /opcodeCopyLoop :loopLbl8 @noOpcodesToCopy /rdi /rbp :movqRegReg # copy new references :quoteEncodingBufferObjects /rdi :movqImmReg /r15 /rsi :movqMemReg /rsi /ecx :movlMemReg 8 /rsi :addqImm8Reg 3 /rcx :shrqImm8Reg :reprcx :movsq /rbp /rdi :xchgqRegReg ::internalAllocateCodeFromEncodingBuffer /rax :movqImmReg /rax :callqReg # patch old code 16 /r15 /rdi :movqMemDisp8Reg 8 /rdi /rbp :movqMemDisp8Reg 16 1 /rbp /rdi /rbp :leaqMemIndexScaleDisp8Reg # CHECK /rdi /ecx :movlMemReg /rdi /rcx :addqRegReg /rcx /rbp :cmpqRegReg /patchSpaceAvailable :jbLbl8 "attepmting to patch reference, but no space is available" ::outputError :ud2 @patchSpaceAvailable # END CHECK 16 /rdi :addqImm8Reg [ /rax :movqImmOOBReg ] ::loadToRdi 16 /rax :addqImm8Reg /rax /rdi :movqRegMem 16 /rax :subqImm8Reg /rax 0 /rbp :movqRegMemDisp8 8 /rdi :addqImm8Reg 8 /rbp :addqImm8Reg [ /rax :jmpqReg ] ::loadToRdi # old references still around (and possibly used by later code) the # garbage collector will detect forward-only code and react accordingly, # but will correctly follow all references when hitting the code from # stack data (e.g. as return address) 24 /r15 :pushqMemDisp8 32 /r15 :addqImm8Reg :retn ]] /eyreplace defv # create unscoped, untyped function from opcode array # 0 -> new opcodes as integer array # 1 -> new references as array [[ 24 /r15 :subqImm8Reg 16 /r15 :popqMemDisp8 # store return address /r15 :popqMem # new references source 8 /r15 :popqMemDisp8 # new opcode source # copy new opcodes :quoteEncodingBufferCode /rdi :movqImmReg 8 /r15 /rsi :movqMemDisp8Reg /rsi /ecx :movlMemReg 8 /rsi :addqImm8Reg 3 /rcx :shrqImm8Reg /rcx :decqReg /noOpcodesToCopy :jzLbl8 @opcodeCopyLoop /rsi /rax :movqMemReg /rax ::unboxInteger :stosb 8 /rsi :addqImm8Reg /opcodeCopyLoop :loopLbl8 @noOpcodesToCopy /rdi /rbp :movqRegReg # copy new references :quoteEncodingBufferObjects /rdi :movqImmReg /r15 /rsi :movqMemReg /rsi /ecx :movlMemReg 8 /rsi :addqImm8Reg 3 /rcx :shrqImm8Reg :reprcx :movsq /rbp /rdi :xchgqRegReg ::internalAllocateCodeFromEncodingBuffer /rax :movqImmReg /rax :callqReg # create function object /rax /rdi :movqRegReg /rsi /rsi :xorqRegReg # non-capturing /rdx /rdx :xorqRegReg # untyped ::internalAllocateFunction /rax :movqImmReg /rax :callqReg # rax == function object on heap /rax :pushqReg 16 /r15 :pushqMemDisp8 24 /r15 :addqImm8Reg :retn ]] /eycreateFunction defv # converts integer to float # 0 -> integer value # 0 <- same value as float [[ /rbx :popqReg ::internalAllocateFloat /rax :movqImmReg /rax :callqReg /rdx :popqReg /rdx ::unboxInteger /rax :pushqReg # save float object /rdx 8 /rax :movqRegMemDisp8 # save integer value to memory 8 /rax :fildqMemDisp8 # load integer value from memory 8 /rax :fstp64MemDisp8 # save float value to memory /rbx :pushqReg :retn ]] /eyintToFloat defv # returns internalAllocateInteger into userspace # 0 <- ::internalAllocateInteger as integer [[ /rbx :popqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rax :pushqReg ::internalAllocateInteger /rdx :movqImmReg /rdx 8 /rax :movqRegMemDisp8 /rbx :pushqReg :retn ]] /eyinternalAllocateInteger defv # returns internalAllocateString into userspace # 0 <- ::internalAllocateString as integer [[ /rbx :popqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rax :pushqReg ::internalAllocateString /rdx :movqImmReg /rdx 8 /rax :movqRegMemDisp8 /rbx :pushqReg :retn ]] /eyinternalAllocateString defv # returns internalAllocateFloat into userspace # 0 <- ::internalAllocateFloat as integer [[ /rbx :popqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rax :pushqReg ::internalAllocateFloat /rdx :movqImmReg /rdx 8 /rax :movqRegMemDisp8 /rbx :pushqReg :retn ]] /eyinternalAllocateFloat defv # returns internalAllocateScope into userspace # 0 <- ::internalAllocateScope as integer [[ /rbx :popqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rax :pushqReg ::internalAllocateScope /rdx :movqImmReg /rdx 8 /rax :movqRegMemDisp8 /rbx :pushqReg :retn ]] /eyinternalAllocateScope defv # (template) program boot sequence after freeze [[ /r8 :movqImmOOBReg %EE %EE %EE %EE %EE %EE %EE %EE # 10 /r15 :movqImmOOBReg %EE %EE %EE %EE %EE %EE %EE %EE # 20 ::heapSize /rax :movqImmReg # 30 /rbx :movqImmOOBReg %EE %EE %EE %EE %EE %EE %EE %EE # 40 /rbx /rax :movqRegMem # 43 ::unusedHeapStart /rax :movqImmReg # 53 /rbx :movqImmOOBReg %EE %EE %EE %EE %EE %EE %EE %EE # 63 /rbx /rax :movqRegMem # 66 0 /rax :movqImmReg # 76 # TODO remove this line once r14 scoping is stable /r14 :movqImmOOBReg %EE %EE %EE %EE %EE %EE %EE %EE # 86 /rbx /rax :movqRegReg # 89 # TODO remove this line once r14 scoping is stable :globalAllocations .base /rax :movqImmReg # 99 /rbx :movqImmOOBReg %EE %EE %EE %EE %EE %EE %EE %EE # 109 /rbx /rax :movqRegMem # 112 /r13 :movqImmOOBReg %EE %EE %EE %EE %EE %EE %EE %EE # 122 ::garbageCollectStack /rax :movqImmReg 0 /rax :andqImm8Mem ::freeLists /rax :movqImmReg ::FREELISTCOUNT { 0 /rax :andqImm8Mem 8 /rax :addqImm8Reg } rep [ { sys .?linux } { /r8 /rsp :xchgqRegReg # swap to elymas stack to ensure correct GC behavior for what follows } { sys .?freebsd } { /r8 /rsp :movqRegReg # swap to elymas stack to ensure correct GC behavior for what follows /rdi /r8 :movqRegReg # FreeBSD passes argc stack location in rdi } ] conds # empty encoding buffer to ensure the GC does not follow residue from freeze into unallocated memory :quoteEncodingBufferObjects /rdi :movqImmReg /rax /rax :xorqRegReg /rax /rdi :movqRegMem # setup new sys .argv /r8 /rdi :movqMemReg # get argc 8 /r8 :addqImm8Reg /rdi :decqReg /rdi :pushqReg 3 /rdi :shlqImm8Reg ::internalAllocateArray /rax :movqImmReg /rax :callqReg # rax == new sys .argv array /rax :pushqReg /rax :pushqReg /rax :movqImmOOBReg "argv" ::string /rax :pushqReg /rax :movqImmOOBReg "sys" ::string /rax :pushqReg /rax :movqImmOOBReg "eyprogramStart" "ey|" ::linkAbs64 /rax :callqReg /rax :movqImmOOBReg "defv" ::string /rax :pushqReg /rax :movqImmOOBReg "eyprogramStart" "ey." ::linkAbs64 /rax :callqReg /rax :popqReg 8 /rax /rbx :leaqMemDisp8Reg /rcx :popqReg 8 /r8 :addqImm8Reg # throw away program name # TODO: save it somewhere /rcx /rcx :testqRegReg /argvAssemblyDone :jzLbl8 @argvAssembly /r8 /rdi :movqMemReg # load next argv element 8 /r8 :addqImm8Reg /rcx :pushqReg /copyCString :callqLbl32 /rax /rbx :movqRegMem 8 /rbx :addqImm8Reg /rcx :popqReg /argvAssembly :loopLbl8 @argvAssemblyDone # call frozen function |ey* /rax :movqImmReg /rax :callqReg :ud2 @copyCString # rdi -> pointer to beginning of string # rax <- pointer to new elymas string /rax /rax :xorqRegReg /rcx /rcx :xorqRegReg /rcx :decqReg /rdi /rsi :movqRegReg /rdi :pushqReg :repnz :scasb /rcx :negqReg 2 /rcx :subqImm8Reg /rcx /rdi :movqRegReg ::internalAllocateString /rax :movqImmReg /rax :callqReg /rsi :popqReg 16 /rax /rcx :movqMemDisp8Reg 24 /rax /rdi :leaqMemDisp8Reg :reprcx :movsb :retn ]] /eyprogramStart defv > _ ==globalFunctions { defv }' ::allocateOffsetStruct < # patch programStart to current program state # this function must be called first in sys .freeze because it has to unwind the exactly # correct number of things from the stack to make the sys .freeze execution transparent # TODO: actually do this (e.g. by recoding the freeze startup in assembly) # TODO: ... for now just flush the call stack on freeze # returns # 0 <- the number of allocations according to frozen alloc list fill state # 1 <- the current heap size (to detect re-allocs during freeze) [[ /rbx :popqReg eyprogramStart /rax :movqImmReg /rsp 2 /rax :movqRegMemDisp8 # /r15 12 /rax :movqRegMemDisp8 # TODO: something like this (but correctly adjusted) would be right # TODO whereas this just flushes the stack /r15 /rdx :movqRegReg :STACKBOTTOMMARKER /rcx :movqImmReg /cmpCallStackStart :jmpLbl8 @scanCallStackStart 8 /rdx :addqImm8Reg @cmpCallStackStart /rcx /rdx :cmpqRegMem /scanCallStackStart :jnzLbl8 /rdx 12 /rax :movqRegMemDisp8 ::heapSize /rdx :movqImmReg /rdx /rdx :movqMemReg /rdx 32 /rax :movqRegMemDisp8 ::unusedHeapStart /rdx :movqImmReg /rdx /rdx :movqMemReg /rdx 55 /rax :movqRegMemDisp8 /r14 /rdx :movqRegReg 16 /rdx /rdx :movqMemDisp8Reg # unwind one scope /rdx 78 /rax :movqRegMemDisp8 :globalAllocations .base /rdx :movqImmReg /rdx /rdx :movqMemReg /rdx :pushqReg # store allocation count for later return value /rdx 101 /rax :movqRegMemDisp8 /r13 114 /rax :movqRegMemDisp8 ::heapSize /rdx :movqImmReg /rdx /rdx :movqMemReg /rdx :pushqReg # store heap size for later return value ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 # save heap size to return integer /rdx :popqReg /rax :pushqReg /rdx :pushqReg # shuffle stack to access alloc count ::internalAllocateInteger /rax :movqImmReg /rax :callqReg # type zero does not need to be changed /rdx :popqReg 4 /rdx :shrqImm8Reg /rdx :decqReg /rdx 8 /rax :movqRegMemDisp8 # store value /rax :pushqReg /rbx :pushqReg :retn ]] /eypatchProgramStart defv > _ ==globalFunctions2 { defv }' ::allocateOffsetStruct { | }' ::linkResolve "asm" enterSubScope [ globalFunctions keys eydeff { | }' createScopeEntries globalFunctions2 keys eydeff { | }' createScopeEntries createScopeExtensionEntries ] :execute leaveSubScope > -- # vim: syn=elymas