diff options
| author | Drahflow <drahflow@gmx.de> | 2014-02-18 18:07:19 +0100 |
|---|---|---|
| committer | Drahflow <drahflow@gmx.de> | 2014-02-18 18:07:19 +0100 |
| commit | 676307d3cc203f3a5e583cdc96c37c7821f79452 (patch) | |
| tree | 320fb1fa91ada14f46bd3b06f966bf08a52462a7 /compiler | |
| parent | b66ee5f07c7642b3b92cf2b820823a1564b48a8e (diff) | |
Coroutines.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/elymasAsm.ey | 92 | ||||
| -rw-r--r-- | compiler/elymasAsmLib.ey | 148 | ||||
| -rw-r--r-- | compiler/elymasAsmOps.ey | 9 | ||||
| -rw-r--r-- | compiler/elymasGlobal.ey | 264 | ||||
| -rw-r--r-- | compiler/elymasGlobalSysAsm.ey | 12 |
5 files changed, 483 insertions, 42 deletions
diff --git a/compiler/elymasAsm.ey b/compiler/elymasAsm.ey index b5099ac..265ff95 100644 --- a/compiler/elymasAsm.ey +++ b/compiler/elymasAsm.ey @@ -2,7 +2,10 @@ 4096 ==PAGESIZE 4096 16 mul 8 mul ==STACKSIZE 65536 ==GLOBALALLOCSSIZE - 128 ==STACKSTART + 16 ==STACKSTART + + 6148914691236517205 ==STACKBOTTOMMARKER + 4 ==ERRORMARKER # hex decoding { @@ -59,23 +62,50 @@ { allocs .base } /base deff > ==globalAllocations - # global stack layout - # 0 - STACKSTART : global variables - # %0 : current stack pointer - # STACKSTART - ...: real stack - [ /mainStack /mainCallStack ] { + # stack layout + # %0 : stack size + # %8 : current stack pointer + # ... : real stack + # <end> : stack bottom marker + [ /bootStack /bootCallStack ] { < - STACKSIZE alloc _ globalAllocations .register + PAGESIZE alloc _ globalAllocations .register ==stack stack .base ==i [ - stack .base STACKSIZE add imm64 + PAGESIZE imm64 + stack .base PAGESIZE add 8 sub imm64 + ] { i sys .asm .poke i 1 add =i } each + + stack .base PAGESIZE add 8 sub =i + [ + STACKBOTTOMMARKER imm64 ] { i sys .asm .poke i 1 add =i } each stack > -12 == }' each + PAGESIZE alloc _ globalAllocations .register + ==:initialCoroutine + + # TODO create a heap-based initial coroutine + < + initialCoroutine .base ==i + [ + 40 0 0 0 0 0 0 %C8 + 0 imm64 # no instruction pointer, will be driven via executeOn + 0 imm64 # no scope pointer, will be installed by elymalGlobal into r14 + bootCallStack .base imm64 + bootStack .base imm64 + # 40 bytes so far, coroutine image stops here + # 40-47: original rsp value + 0 imm64 + # 48-55: currently running co-routine (so elymasGlobal can switch to heap allocated one) + initialCoroutine .base imm64 + ] { i sys .asm .poke i 1 add =i } each + > -- + STACKSIZE alloc _ globalAllocations .register .base ==:quoteEncodingBufferCode @@ -89,8 +119,8 @@ codearea } /arrayToCode deff - # take an array of instruction bytes and execute it on the given stack - { ==callStack ==valueStack ==opcodes + # take an array of instruction bytes and execute it in the given coroutine context + { ==opcodes [ /rbx pushqReg /rbp pushqReg @@ -98,19 +128,29 @@ /r13 pushqReg /r14 pushqReg /r15 pushqReg - valueStack /rbx movqImmReg - /rsp /rbx xchgqRegMem - callStack /rbx movqImmReg - /r15 /rbx xchgqRegMem - /r14 8 /rbx xchgqRegMemDisp8 + initialCoroutine .base 40 add /rbx movqImmReg + /rsp /rbx movqRegMem + + initialCoroutine .base 48 add /rbx movqImmReg + /rbx /r13 movqMemReg + 16 /r13 /r14 movqMemDisp8Reg + 24 /r13 /rbx movqMemDisp8Reg + 8 /rbx /r15 movqMemDisp8Reg + 32 /r13 /rbx movqMemDisp8Reg + 8 /rbx /rsp movqMemDisp8Reg opcodes _ len dearray - callStack /rbx movqImmReg - /r15 /rbx xchgqRegMem - /r14 8 /rbx xchgqRegMemDisp8 - valueStack /rbx movqImmReg - /rsp /rbx xchgqRegMem + /r14 16 /r13 movqRegMemDisp8 + 24 /r13 /rbx movqMemDisp8Reg + /r15 8 /rbx movqRegMemDisp8 + 32 /r13 /rbx movqMemDisp8Reg + /rsp 8 /rbx movqRegMemDisp8 + initialCoroutine .base 48 add /rbx movqImmReg + /r13 /rbx movqRegMem + + initialCoroutine .base 40 add /rbx movqImmReg + /rbx /rsp movqMemReg /r15 popqReg /r14 popqReg /r13 popqReg @@ -119,16 +159,12 @@ /rbx popqReg retn ] - } /compileOn deff + } /compile deff { - compileOn arrayToCode _ .base sys .asm .execute - .free - } /executeOn deff - - { mainStack .base mainCallStack .base executeOn } /execute deff - - { mainStack .base mainCallStack .base compileOn } /compile deff + compile arrayToCode _ .base sys .asm .execute + .free + } /execute deff > /assembler defv # vim: syn=elymas diff --git a/compiler/elymasAsmLib.ey b/compiler/elymasAsmLib.ey index 15cbfbd..b619bfd 100644 --- a/compiler/elymasAsmLib.ey +++ b/compiler/elymasAsmLib.ey @@ -349,22 +349,33 @@ /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 - :mainStack .base :STACKSIZE add /rsi :movqImmReg - @loopThroughMainStack - 8 /rsi :subqImm8Reg + :STACKBOTTOMMARKER /rcx :movqImmReg + /rsp /rsi :movqRegReg + /rcx /rsi :cmpqRegMem + /dataStackEmpty :jzLbl8 + @loopThroughDataStack /rsi /rdi :movqMemReg /markStackObject :callqLbl32 - /rsi /rsp :cmpqRegReg - /loopThroughMainStack :jbLbl8 + 8 /rsi :addqImm8Reg + /rcx /rsi :cmpqRegMem + /loopThroughDataStack :jnzLbl8 + @dataStackEmpty - :mainCallStack .base :STACKSIZE add /rsi :movqImmReg + /r15 /rsi :movqRegReg + /rcx /rsi :cmpqRegMem + /callStackEmpty :jzLbl8 @loopThroughCallStack - 8 /rsi :subqImm8Reg /rsi /rdi :movqMemReg /markStackObject :callqLbl32 - /rsi /r15 :cmpqRegReg - /loopThroughCallStack :jbLbl8 + 8 /rsi :addqImm8Reg + /rcx /rsi :cmpqRegMem + /loopThroughCallStack :jnzLbl8 + @callStackEmpty # start from encoding buffer and mark all reachable blocks :quoteEncodingBufferObjects /rsi :movqImmReg @@ -487,6 +498,10 @@ /markScope :jzLbl32 /rax :decqReg /markNameTable :jzLbl32 + /rax :decqReg + /markStack :jzLbl32 + /rax :decqReg + /markCoroutine :jzLbl32 @markInvalidType /rax /rbx :movqRegReg # for easier inspection @@ -669,6 +684,48 @@ /rcx :popqReg :retn + @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 + + @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 @@ -785,6 +842,9 @@ %90 /al :cmpbImmReg /isScope :jeLbl8 + /rdi :pushqReg + :ERRORMARKER /rax :movqImmReg + /rax :pushqReg "object resolving in is not a scope" outputError :ud2 @@ -879,6 +939,33 @@ /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 @@ -1043,6 +1130,49 @@ :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 < diff --git a/compiler/elymasAsmOps.ey b/compiler/elymasAsmOps.ey index 1a7cc9b..a9f47bc 100644 --- a/compiler/elymasAsmOps.ey +++ b/compiler/elymasAsmOps.ey @@ -55,8 +55,8 @@ } /rex deff { ==mem ==reg - mem [ /spl /sp /esp /rsp /bpl /bp /ebp /rbp ] eq any { - "modrm00 not possible on rsp / rbp and their partial registers" die + mem [ /spl /sp /esp /rsp /bpl /bp /ebp /rbp /r12b /r12w /r12d /r12 /r13b /r13w /r13d /r13 ] eq any { + "modrm00 not possible on rsp / rbp / r12 / r13 and their partial registers" die } rep %00 @@ -504,6 +504,7 @@ /bts %0F %BA /five defAsmBtqImm /bsf %0F %BC defAsmBsfq +/bsr %0F %BD defAsmBsfq { ==off %E8 @@ -1332,6 +1333,10 @@ memoryAddressingVariants keys { ==variant memoryAddressingVariants variant . =*p } /shrqImm8Reg deff { + %FD +} /std deff + +{ %AA } /stosb deff diff --git a/compiler/elymasGlobal.ey b/compiler/elymasGlobal.ey index e64bc05..0e324f2 100644 --- a/compiler/elymasGlobal.ey +++ b/compiler/elymasGlobal.ey @@ -502,6 +502,9 @@ /arrayFunction :jmpLbl32 @unexecutable + /rdx :pushqReg + :ERRORMARKER /rax :movqImmReg + /rax :pushqReg "not an executable thing" ::outputError :ud2 @@ -2414,6 +2417,244 @@ /rax :callqReg /end :jmpLbl8 ]] /eydom defv + + # create a coroutine with empty call stack and data stack + # 0 -> function to execute (type is ignored) + [[ + 8 /r15 :subqImm8Reg + /r15 :popqMem + + /rax :popqReg + 8 /rax /rsi :movqMemDisp8Reg + 24 /rax /rdi :movqMemDisp8Reg + 58 /rdi :btsqImm8Mem # prevent these opcodes from being optimized + 16 /rdi :addqImm8Reg + + # test for and skip initial popping of return address, there is nothing to return to + # leaving them in results in gobbling up of the first passed data item + %49 0 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl8 + %83 1 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl8 + %EF 2 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl8 + %08 3 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl8 + %49 4 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl8 + %8F 5 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl8 + %07 6 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl8 + + 7 /rdi :addqImm8Reg + ::internalAllocateCoroutine /rax :movqImmReg + /rax :callqReg + /rax :pushqReg + + /r15 :pushqMem + 8 /r15 :addqImm8Reg + :retn + + @wrongOpcodeSequence + "could not parse function start while preparing coroutine" ::outputError + :ud2 + ]] /ey!! defv + + # create a coroutine with copied data and call stack + # push said coroutine to stack + # then continue execution at specified function + # 0 -> function to execute within new coroutine (type is ignored) + # 1 -> function to execute outside of new coroutine + [[ + 8 /r15 :subqImm8Reg + /r15 :popqMem + + /rax :popqReg + 8 /rax /rsi :movqMemDisp8Reg + 24 /rax /rdi :movqMemDisp8Reg + 58 /rdi :btsqImm8Mem # prevent these opcodes from being optimized + 16 /rdi :addqImm8Reg + + # test for and skip initial popping of return address + # leaving them in results in gobbling up of the first passed data item + # instead put the return address on the call stack + %49 0 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl32 + %83 1 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl32 + %EF 2 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl32 + %08 3 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl32 + %49 4 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl32 + %8F 5 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl32 + %07 6 /rdi :cmpbImmMemDisp8 + /wrongOpcodeSequence :jnzLbl32 + + 7 /rdi :addqImm8Reg + ::internalAllocateCoroutine /rax :movqImmReg + /rax :callqReg + /rax /rbx :movqRegReg + + ::internalAllocateStack /rax :movqImmReg + /rax :callqReg + /rax 24 /rbx :movqRegMemDisp8 + + # copy call stack + :STACKBOTTOMMARKER /rdx :movqImmReg + /r15 /rsi :movqRegReg + /rdx /rsi :cmpqRegMem + /callStackEmpty :jzLbl8 + @scanCallStack + 8 /rsi :addqImm8Reg + /rdx /rsi :cmpqRegMem + /scanCallStack :jnzLbl8 + + 8 /rax /rdi :movqMemDisp8Reg + /rsi /rcx :movqRegReg + /r15 /rcx :subqRegReg + 3 /rcx :shrqImm8Reg + /rcx :incqReg + :std + :reprcx :movsq + :cld + 8 /rdi :addqImm8Reg + /rdi 8 /rax :movqRegMemDisp8 + @callStackEmpty + + ::internalAllocateStack /rax :movqImmReg + /rax :callqReg + /rax 32 /rbx :movqRegMemDisp8 + + # copy data stack + :STACKBOTTOMMARKER /rdx :movqImmReg + /rsp /rsi :movqRegReg + /rdx /rsi :cmpqRegMem + /dataStackEmpty :jzLbl8 + @scanDataStack + 8 /rsi :addqImm8Reg + /rdx /rsi :cmpqRegMem + /scanDataStack :jnzLbl8 + + 8 /rax /rdi :movqMemDisp8Reg + /rsi /rcx :movqRegReg + /rsp /rcx :subqRegReg + 3 /rcx :shrqImm8Reg + :std + :reprcx :movsq + :cld + 8 /rdi :addqImm8Reg + /rdi 8 /rax :movqRegMemDisp8 + @dataStackEmpty + + /rax :popqReg + /rbx :pushqReg + /rax :pushqReg + + /r15 :pushqMem + 8 /r15 :addqImm8Reg + + |ey* /rax :movqImmReg + /rax :jmpqReg + + @wrongOpcodeSequence + "could not parse function start while preparing coroutine" ::outputError + :ud2 + ]] /ey!!' defv + + # pass control to a coroutine + # 0 -> number of stack elements to copy over + # 1 -> coroutine to continue (may also be some other executable, in which case no stack passing is done) + # 2... -> passed stack arguments + [[ + /rbx :popqReg + + /rcx :popqReg + /rbp :popqReg + /rcx :pushqReg + + 7 /rbp /cl :movbMemDisp8Reg + %F0 /cl :andbImmReg + %C0 /cl :cmpbImmReg + /coroutineCase :jzLbl8 + + "type of object not handled in !" ::outputError + :ud2 + + @coroutineCase + 0 24 /rbp :cmpqImm8MemDisp8 + /targetCallStackExists :jnzLbl8 + /rbp :pushqReg + ::internalAllocateStack /rax :movqImmReg + /rax :callqReg + /rbp :popqReg + /rax 24 /rbp :movqRegMemDisp8 + @targetCallStackExists + + 0 32 /rbp :cmpqImm8MemDisp8 + /targetDataStackExists :jnzLbl8 + /rbp :pushqReg + ::internalAllocateStack /rax :movqImmReg + /rax :callqReg + /rbp :popqReg + /rax 32 /rbp :movqRegMemDisp8 + @targetDataStackExists + + /rcx :popqReg + /rcx ::unboxInteger + + # move stack data + /rsp /rsi :movqRegReg + + 32 /rbp /rdi :movqMemDisp8Reg + 8 /rdi /rdi :movqMemDisp8Reg + /rcx /rdx :movqRegReg + 3 /rdx :shlqImm8Reg + /rdx /rdi :subqRegReg + /rdi :pushqReg + /rdi :pushqReg + 32 /rbp /rdi :movqMemDisp8Reg + 8 /rdi :popqMemDisp8 + /rdi :popqReg + /rdx /rsp :addqRegReg + :reprcx :movsq + + # save old state + /rbx 8 /r13 :movqRegMemDisp8 + /r14 16 /r13 :movqRegMemDisp8 + 24 /r13 /rax :movqMemDisp8Reg + /r15 8 /rax :movqRegMemDisp8 + 32 /r13 /rax :movqMemDisp8Reg + /rsp 8 /rax :movqRegMemDisp8 + + # exclude source opcodes from being optimized away + /rbp :pushqReg + /rbx /rdi :movqRegReg + ::internalObjectStart /rax :movqImmReg + /rax :callqReg + /rax /rax :testqRegReg + /nonHeapCoroutine :jzLbl8 + 58 /rax :btsqImm8Mem + @nonHeapCoroutine + /rbp :popqReg + + # load new state + 8 /rbp /rbx :movqMemDisp8Reg + 16 /rbp /r14 :movqMemDisp8Reg + 24 /rbp /rax :movqMemDisp8Reg + 8 /rax /r15 :movqMemDisp8Reg + 32 /rbp /rax :movqMemDisp8Reg + 8 /rax /rsp :movqMemDisp8Reg + + /r13 :pushqReg # push caller to target stack + /rbp /r13 :movqRegReg + + /rbx :pushqReg + :retn + ]] /ey! defv > _ ==globalFunctions3 { defv }' ::allocateOffsetStruct < @@ -3014,7 +3255,26 @@ } each } /createScopeExtensionEntries deff - [ + [[ + /afterRipInitialization :jmpLbl8 + @ripInitialization + /rdi :popqReg + /rsi /rsi :xorqRegReg + ::internalAllocateCoroutine /rax :movqImmReg + /rax :callqReg + /rax :pushqReg + + /rdx /rdx :xorqRegReg + 63 /rdx :btsqImm8Reg + /rdx :pushqReg + + |ey! /rax :movqImmReg + /rax :callqReg # switch over to heap-allocated coroutine + + @afterRipInitialization + /ripInitialization :callqLbl32 + /rax :popqReg # drop initial coroutine reference + globalFunctions keys len globalFunctions2 keys len add globalFunctions3 keys len add @@ -3034,7 +3294,7 @@ globalMacros keys eydefq { | }' createScopeEntries globalT11t1Functions keys eydeffd t11t1 { | }' createTypedScopeEntries globalT1t1Functions keys eydeffd t1t1 { | }' createTypedScopeEntries - ] :execute + ]] :execute { ==name [[ diff --git a/compiler/elymasGlobalSysAsm.ey b/compiler/elymasGlobalSysAsm.ey index e865f34..97d4bb7 100644 --- a/compiler/elymasGlobalSysAsm.ey +++ b/compiler/elymasGlobalSysAsm.ey @@ -492,6 +492,7 @@ :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 /r8 /rsp :xchgqRegReg # swap to elymas stack to ensure correct GC behavior for what follows @@ -589,7 +590,15 @@ eyprogramStart /rax :movqImmReg /rsp 2 /rax :movqRegMemDisp8 # /r15 12 /rax :movqRegMemDisp8 # TODO: something like this (but correctly adjusted) would be right - :mainCallStack .base :STACKSIZE add /rdx :movqImmReg # TODO whereas this just flushes the stack + # 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 @@ -604,6 +613,7 @@ /rdx /rdx :movqMemReg /rdx :pushqReg # store allocation count for later return value /rdx 101 /rax :movqRegMemDisp8 + /r13 114 /rax :movqRegMemDisp8 ::internalAllocateInteger /rax :movqImmReg /rax :callqReg |
