< txt .consume .|hu "%" defq 1 ==ARRAYMARKER # FIXME unify with elymasGlobal.ey 3 ==POSITIONMARKER # FIXME unify with elymasGlobal.ey 4 ==INITIALSCOPESIZE # FIXME take this from the compiler directory sys .asm .ops ==:sysasmops { quoted { _ sys .typed .type 1 eq { sysasmops -01 .| } { "sysasmops" "|" | -102 "." | } ? * } { sysasmops -01 . } ? * } ":" defq { :labelRecord [ } "[[" deff { ] :labelResolve } "]]" deff sysasmops .|label "@" deff sys .asm "::" via sys .asm .|peek ==:peek { [ -01 _ 4 add range peek each ] 256 math .unbase } =*:peeku32 { [ -01 _ 8 add range peek each ] 256 math .unbase } =*:peeku64 [ 8 /r15 :subqImm8Reg /r15 :popqMem ] ==:generalHeaderPattern [ 8 /r15 :subqImm8Reg /r14 /r15 :movqRegMem /r14 /rsi :movqRegReg 0 /rdi :movqImmReg # this should match arbitrary INITIALSCOPESIZE, hence 0 0 /rax :movqImmReg /rax :callqReg /rax /r14 :movqRegReg ] ==:scopingHeaderPattern [ 0 /rax :movqImmReg /rax :pushqReg ] ==:pushConstantPattern [ 0 /rax :movqImmReg /rax :callqReg ] ==:callConstantPattern [ 0 :callqRel32 ] ==:footerPattern [ /rbx :popqReg 0 /rax :movqImmReg /rax :pushqReg /rbx :pushqReg 0 /rax :movqImmReg /rax :jmpqReg ] ==:constantActiveGeneralPattern [ 16 /r15 :subqImm8Reg 8 /r15 :popqMemDisp8 /r14 /r15 :movqRegMem 0 /r14 :movqImmReg 0 /rax :movqImmReg /rax :callqReg /r15 /r14 :movqMemReg 8 /r15 :pushqMemDisp8 16 /r15 :addqImm8Reg :retn ] ==:constantNormalFunctionScopedUntypedPattern [ 0 /rax :movqImmReg /rax :jmpqReg ] ==:constantNormalFunctionUnscopedUntypedPattern [ /rbx :popqReg 0 /rax :movqImmReg /rax :pushqReg /rbx :jmpqReg ] ==:constantPassivePattern [ /r14 /rax :movqRegReg ] ==:staticLoadPattern [ 16 /rax /rax :movqMemDisp8Reg ] ==:staticLoadParentPattern [ /rbx :popqReg 0 /rax /rax :movqMemDisp32Reg ] ==:staticLoadFromScopePattern [ 24 /rax /rcx :movqMemDisp8Reg # load extension area pointer /rbx :popqReg 0 /rcx /rax :movqMemDisp32Reg # load entry ] ==:staticLoadFromExtensionPattern [ /rax :pushqReg 0 /rcx :movqImmReg /rax /rcx :movqRegMem /rbx :jmpqReg ] ==:staticLoadPassivePattern [ /rax :pushqReg 0 /rcx :movqImmReg /rax /rcx :movqRegMem /rbx :pushqReg 0 /rax :movqImmReg /rax :jmpqReg ] ==:staticLoadActivePattern [ 8 /r15 :subqImm8Reg /r15 :popqMem 0 /rax :movqImmReg /rax :pushqReg /r15 :pushqMem 8 /r15 :addqImm8Reg :retn ] ==:customFunctionConstantPassivePattern [ 8 /r15 :subqImm8Reg /r15 :popqMem 0 /rdi :movqImmReg %48 16 /rdi :cmpbImmMemDisp8 ] ==:customFunctionObjectCreationHeaderPattern [ 8 /r15 :subqImm8Reg /r15 :popqMem 0 /rdi :movqImmReg 0 :jmpRel8 ] ==:customFunctionObjectCreationHeaderPatternPatched [ 8 /r15 :subqImm8Reg /r15 :popqMem ] ==:customFunctionHeaderPattern /NOP ==:NOP /PUSH ==:PUSH /CALL ==:CALL /CALLSCOPED ==:CALLSCOPED /FUNCTIONCREATE ==:FUNCTIONCREATE /STATIC ==:STATIC /STATICTYPED ==:STATICTYPED /STATICWRITE ==:STATICWRITE /STATICDOT ==:STATICDOT /STATICTYPEDDOT ==:STATICTYPEDDOT /NATIVE ==:NATIVE /NATIVENOSCOPE ==:NATIVENOSCOPE /ARRAYSTAR ==:ARRAYSTAR /UNTYPEDSCOPEDSTAR ==:UNTYPEDSCOPEDSTAR /UNTYPEDUNSCOPEDSTAR ==:UNTYPEDUNSCOPEDSTAR /STRINGSTAR ==:STRINGSTAR /CONDITIONALTAIL ==:CONDITIONALTAIL /ARRAYCLEAR ==:ARRAYCLEAR /RAWCONSTREP ==:RAWCONSTREP /RAWCONSTREPTHISSCOPE ==:RAWCONSTREPTHISSCOPE /RAWCONSTEACHTHISSCOPE ==:RAWCONSTEACHTHISSCOPE /RAWCONSTLOOPTHISSCOPE ==:RAWCONSTLOOPTHISSCOPE /RAWCONSTCONDSTHISSCOPE ==:RAWCONSTCONDSTHISSCOPE /INLINEQUESTIONSTARTHISSCOPE ==:INLINEQUESTIONSTARTHISSCOPE /INLINEQUESTIONSTARUNSCOPEDCONST ==:INLINEQUESTIONSTARUNSCOPEDCONST { =*f ==t t { f } { 0 } ? * } /andif deffd [ NATIVENOSCOPE [[ /rcx :popqReg /rdx :popqReg /rcx /rax :movqRegReg /rdx /rbp :movqRegReg 63 /rax :btrqImm8Reg /nonInlineCase :jncLbl8 63 /rbp :btrqImm8Reg /nonInlineCase :jncLbl8 /rax /rax :testqRegReg /negative :jzLbl8 /rbp /rbp :testqRegReg /negative :jzLbl8 1 /rax :movqImmReg 63 /rax :btsqImm8Reg /rax :pushqReg /done :jmpLbl8 @negative /rax /rax :xorqRegReg 63 /rax :btsqImm8Reg /rax :pushqReg /done :jmpLbl8 @nonInlineCase /rdx :pushqReg /rcx :pushqReg |and ::rawAddress /rax :movqImmReg /rax :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg @done ]] ] ==:INLINEBLOCKAND [ NATIVENOSCOPE [[ /rcx :popqReg /rdx :popqReg /rcx /rax :movqRegReg /rdx /rbp :movqRegReg 63 /rax :btrqImm8Reg /nonInlineCase :jncLbl8 63 /rbp :btrqImm8Reg /nonInlineCase :jncLbl8 /rax /rax :testqRegReg /positive :jnzLbl8 /rbp /rbp :testqRegReg /positive :jnzLbl8 /rax /rax :xorqRegReg 63 /rax :btsqImm8Reg /rax :pushqReg /done :jmpLbl8 @positive 1 /rax :movqImmReg 63 /rax :btsqImm8Reg /rax :pushqReg /done :jmpLbl8 @nonInlineCase /rdx :pushqReg /rcx :pushqReg |or ::rawAddress /rax :movqImmReg /rax :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg @done ]] ] ==:INLINEBLOCKOR [ NATIVENOSCOPE [[ /rcx :popqReg /rcx /rax :movqRegReg 63 /rax :btrqImm8Reg /nonInlineCase :jncLbl8 /rcx /rcx :xorqRegReg /rax /rax :testqRegReg /cl :seteReg 63 /rcx :btsqImm8Reg /rcx :pushqReg /done :jmpLbl8 @positive 1 /rax :movqImmReg 63 /rax :btsqImm8Reg /rax :pushqReg /done :jmpLbl8 @nonInlineCase /rcx :pushqReg |not ::rawAddress /rax :movqImmReg /rax :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg @done ]] ] ==:INLINEBLOCKNOT { ==negatedOpcodeName ==functionName [ NATIVENOSCOPE [[ /rcx :popqReg /rdx :popqReg /rcx /rax :movqRegReg /rdx /rbp :movqRegReg 63 /rax :btrqImm8Reg /nonInlineCase :jncLbl8 63 /rbp :btrqImm8Reg /nonInlineCase :jncLbl8 /rsi /rsi :xorqRegReg /rax /rbp :cmpqRegReg [ /rsi :incqReg ] len negatedOpcodeName : /rsi :incqReg 63 /rsi :btsqImm8Reg /rsi :pushqReg /done :jmpLbl8 @nonInlineCase /rdx :pushqReg /rcx :pushqReg functionName | ::rawAddress /rax :movqImmReg /rax :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg @done ]] ] } /comparisonOperator deffst /eq /jnzRel8 comparisonOperator ==:INLINEBLOCKEQ /neq /jzRel8 comparisonOperator ==:INLINEBLOCKNEQ /le /jnleRel8 comparisonOperator ==:INLINEBLOCKLE /lt /jnlRel8 comparisonOperator ==:INLINEBLOCKLT /ge /jngeRel8 comparisonOperator ==:INLINEBLOCKGE /gt /jngRel8 comparisonOperator ==:INLINEBLOCKGT { ==e [ 0 e * STATICTYPED eq { 4 e * sys .typed .type 0 eq }' andif 0 e * PUSH eq { 1 e * sys .typed .type 0 eq }' andif # FIXME: this is wrong! PUSH holds _address_ of integer, if at all 0 e * STRINGSTAR eq ] any } /holdsInt deffd [ "/" "?" "[" "]" /cat /dearray /die /len /quoted /range "''" ".?" ".?'" ".|" ";" /keys /dom /add /and /band /bor /bxor /div /eq /ge /gt /le /lt /mod /mul /neq /or /sub /udiv /umod /xor /bnot /neg /not "," ",--" "--," "_," ",_" ",---" ] { | ::rawCodeAddress } [ 0 ] [ 0 ] '' * ==:SCOPEIGNORANTFUNCTIONS [ "|" "=" /def /defv /deff /defq /defm /defvs /deffs /defms /defvt /defft /defmt /defvst /deffst /defmst /defvc /deffc /defmc /defvd /deffd /defmd ] { | ::rawCodeAddress } [ 0 ] [ 0 ] '' * ==:SCOPETIGHTFUNCTIONS [ /defvs /deffs /defms /defvst /deffst /defmst ] { | ::rawCodeAddress } [ 0 ] [ 0 ] '' * ==:STATICDEFININGFUNCTIONS { ==o ==executingScope [ ] ==fragileReferences # things the GC should leave alone while we are working on it { [ -01 ] fragileReferences cat =fragileReferences } /protectReference deffst 0 ==containsScopeModifications { _ ==logic logic { ==entry 0 entry * ==action [ { action CALL eq { 1 entry * "<" | ::rawCodeAddress eq }' andif }' { 1 =containsScopeModifications } { action CALL eq { 1 entry * ">" | ::rawCodeAddress eq }' andif }' { 1 =containsScopeModifications } { action CALL eq { 1 entry * ">'" | ::rawCodeAddress eq }' andif }' { 1 =containsScopeModifications } ] conds } each } /testScopeModifications deffst 0 ==allocatedScopeMightEscape { _ ==logic 0 ==needsAScope logic { ==entry 0 entry * ==action [ { action NOP eq }' { } { action PUSH eq }' { } { action CALL eq { 1 entry * SCOPEIGNORANTFUNCTIONS eq any } andif }' { } { action CALL eq { 1 entry * SCOPETIGHTFUNCTIONS eq any } andif }' { 1 =needsAScope } { action CALL eq { 1 entry * 16 sub 7 add peek * %02 band } andif }' { 1 =needsAScope } { action CALLSCOPED eq }' { } { action STATIC eq }' { 1 =needsAScope } { action STATICTYPED eq }' { 1 =needsAScope } { action STATICWRITE eq }' { 1 =needsAScope } { action STATICDOT eq }' { 1 =needsAScope } { action STATICTYPEDDOT eq }' { 1 =needsAScope } { action NATIVENOSCOPE eq }' { 1 =needsAScope } { action ARRAYSTAR eq }' { } { action UNTYPEDSCOPEDSTAR eq }' { } { action STRINGSTAR eq }' { } { action CONDITIONALTAIL eq }' { 1 =needsAScope } { action ARRAYCLEAR eq }' { } { action RAWCONSTREP eq { 1 entry * 16 sub 7 add peek * %02 band } andif }' { 1 =needsAScope } { action RAWCONSTREPTHISSCOPE eq { 1 entry * 16 sub 7 add peek * %02 band } andif }' { 1 =needsAScope } { action RAWCONSTEACHTHISSCOPE eq { 1 entry * 16 sub 7 add peek * %02 band } andif }' { 1 =needsAScope } { action RAWCONSTLOOPTHISSCOPE eq { 1 entry * 16 sub 7 add peek * %02 band } andif { 2 entry * 16 sub 7 add peek * %02 band } andif }' { 1 =needsAScope } # TODO: this is useless currently, as we don't get evaluation on all branches before optimizing here # { action RAWCONSTCONDSTHISSCOPE eq # { [ 1 entry * { ==function # 0 function * FUNCTIONCREATE eq { 2 function * 7 add peek * %02 band }' andif # } each ] all }' andif # }' { /RAWCONSTCONDSTHISSCOPE_tight dump 1 =needsAScope } # { action INLINEQUESTIONSTARTHISSCOPE eq # { 1 entry * 16 sub 7 add peek * %02 band } andif # { 2 entry * 16 sub 7 add peek * %02 band } andif # }' { /INLINEQUESTIONSTARTHISSCOPE_tight dump 1 =needsAScope } # { action INLINEQUESTIONSTARUNSCOPEDCONST eq # { 1 entry * 16 sub 7 add peek * %02 band } andif # { 2 entry * 16 sub 7 add peek * %02 band } andif # }' { /INLINEQUESTIONSTARUNSCOPEDCONST_tight dump 1 =needsAScope } # TODO handle more known-harmless unscoped global functions { 1 }' { 1 =allocatedScopeMightEscape 1 =needsAScope } ] conds } each needsAScope not isScoping and { 0 =isScoping } rep } /testAllocatedScopeEscape deffst 0 ==templateNametable { 1 ==templatable executingScope ::rawAddress 8 add peeku64 ==nameTableAddress nameTableAddress { nameTableAddress peeku32 ==nameTableLength 0 nameTableLength 16 sub 16 div range 16 mul nameTableAddress 16 add add { ==entryAddr entryAddr peeku64 ==keyAddr keyAddr { entryAddr 8 add peeku32 %10 band not { 0 =templatable # "NOT templatable because" dump # nameTableAddress dump # keyAddr ::rawObject dump } rep } { } ? * } each } { 0 =templatable } ? * templatable { nameTableAddress =templateNametable nameTableAddress 7 add _ sys .asm .peek %04 bor -01 sys .asm .poke # set template bit } rep } /testNametableTemplatability deffst { ==logic [ logic { ==entry 0 entry * ==action action CALL eq { 1 entry * "/" | ::rawCodeAddress eq }' andif { # drop call to / } { entry } ? * } each ] } /rewriteSlash deffst { ==logic [ NOP ] ==last [ logic { ==entry 0 entry * ==action [ { action CALL eq { 1 entry * "|" | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif }' { 1 last * ::rawObject ==constant constant executingScope sys .resolveInfo _ ==loadedObject { ==mode -- ==parentCount 32 add ==offsetInScope ==inExtensionArea inExtensionArea 0 gt { offsetInScope inExtensionArea sub 8 add =offsetInScope 1 =inExtensionArea } rep [ { mode 16 div 1 band }' { # static case [ NOP ] =last [ STATIC offsetInScope parentCount inExtensionArea ] =entry } { mode 16 div 4 band }' { # constant case [ NOP ] =last [ PUSH loadedObject ] =entry } ] conds } { # this can happen if resolution was handled by #.| } ? * } ] conds last entry =last } each last ] } /rewriteConstantPipe deffst { ==logic [ NOP ] ==last [ logic { ==entry 0 entry * ==action [ { action CALL eq { 1 entry * "=" | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif }' { 1 last * ::rawObject ==constant constant executingScope sys .resolveInfo { ==mode -- ==parentCount 32 add ==offsetInScope ==inExtensionArea inExtensionArea 0 gt { offsetInScope inExtensionArea sub 8 add =offsetInScope 1 =inExtensionArea } rep mode 16 div 1 band { [ NOP ] =last [ STATICWRITE offsetInScope parentCount inExtensionArea ] =entry } { } ? * } { # this can happen if resolution was handled by #.= } ? * } { templateNametable { action CALL eq }' andif { 1 entry * STATICDEFININGFUNCTIONS eq any }' andif { 0 last * PUSH eq }' andif }' { 1 last * ::rawObject ==constant constant executingScope sys .resolveInfo { ==mode -- ==parentCount 32 add ==offsetInScope ==inExtensionArea inExtensionArea 0 gt { offsetInScope inExtensionArea sub 8 add =offsetInScope 1 =inExtensionArea } rep inExtensionArea not { # FIXME: implement the extension area case, needs to create extension area on write mode 16 div 1 band { [ NOP ] =last [ STATICWRITE offsetInScope parentCount inExtensionArea ] =entry } { } ? * } rep } { executingScope dump executingScope keys dump constant dump "resolution failed" die } ? * } ] conds last entry =last } each last ] } /rewriteConstantAssignment deffst { _ ==logic 2 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last i 2 sub logic * ==secondLast [ { action CALL eq { 1 entry * "." | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif { 0 secondLast * STATICTYPED eq 0 secondLast * STATICTYPEDDOT eq or 0 secondLast * PUSH eq or }' andif }' { 1 last * ::rawObject ==constant 0 secondLast * STATICTYPED eq 0 secondLast * STATICTYPEDDOT eq or { 4 secondLast * } { 1 secondLast * ::rawObject } ? * ==relevantScope constant relevantScope sys .resolveInfo _ ==loadedObject { ==mode -- ==parentCount 32 add ==offsetInScope ==inExtensionArea inExtensionArea 0 gt { offsetInScope inExtensionArea sub 8 add =offsetInScope 1 =inExtensionArea } rep [ { mode 16 div 1 band }' { # static case [ { mode 16 mod 0 eq }' { [ NOP ] i 1 sub logic =[] mode 16 div 2 band { [ STATICTYPEDDOT offsetInScope parentCount inExtensionArea loadedObject ::rawObject ] i logic =[] } { [ STATICDOT offsetInScope parentCount inExtensionArea ] i logic =[] } ? * } { mode 16 mod 1 eq }' { # mode 16 div 2 band { # "FIXME: optimize to call type on typed case" dump # } rep [ STATICDOT offsetInScope parentCount inExtensionArea ] i 1 sub logic =[] [ CALL "*" | ::rawCodeAddress ] i logic =[] } { mode 16 mod 3 eq }' { # TODO: implement member case one day } { 1 }' { # TODO this might conceivably be legal though... "Scope keys: " dump relevantScope keys dump "Constant: " dump constant dump "Mode: " dump mode dump "static . resolution resulted in execution mode neither 0 nor 1" die } ] conds } { mode 16 div 4 band }' { # const case [ { mode 16 mod 0 eq }' { [ NOP ] i 2 sub logic =[] [ NOP ] i 1 sub logic =[] [ PUSH loadedObject ] i logic =[] } { mode 16 mod 1 eq }' { [ NOP ] i 2 sub logic =[] [ PUSH loadedObject ] i 1 sub logic =[] [ CALL "*" | ::rawCodeAddress ] i logic =[] } { mode 16 mod 3 eq }' { # leave scope on stack as stack content for member call [ PUSH loadedObject ] i 1 sub logic =[] [ CALL "*" | ::rawCodeAddress ] i logic =[] } { 1 }' { # TODO this might conceivably be legal though... "Scope keys: " dump relevantScope keys dump "Constant: " dump constant dump "Mode: " dump mode dump "static . resolution resulted in execution mode neither 0 nor 1" die } ] conds } ] conds } { # this can happen if resolution was handled by #. } ? * } ] conds } each } /rewriteConstantDot deffst { _ ==logic 3 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last i 2 sub logic * ==secondLast i 3 sub logic * ==thirdLast action CALL eq { 1 entry * "*" | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif { secondLast holdsInt }' andif { thirdLast holdsInt }' andif { [ { 1 last * |add ::rawAddress eq { 0 secondLast * PUSH eq }' andif { 1 secondLast * 128 lt }' andif }' { 1 secondLast * ==value [ NATIVENOSCOPE [ /rdx :popqReg 63 /rdx :btrqImm8Reg [ 8 /rdx /rdx :movqMemDisp8Reg ] len :jcRel8 8 /rdx /rdx :movqMemDisp8Reg value /rdx :addqImm8Reg /rdx /rax :movqRegReg 32 /rax :shrqImm8Reg [ 63 /rdx :btsqImm8Reg /rdx :pushqReg 0 :jmpRel8 ] len :jnzRel8 63 /rdx :btsqImm8Reg /rdx :pushqReg [ /rdx :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg ] len :jmpRel8 /rdx :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg ] ] i logic =[] [ NOP ] i 1 sub logic =[] [ NOP ] i 2 sub logic =[] } { 1 last * |add ::rawAddress eq }' { [ NATIVENOSCOPE [ /rcx :popqReg 63 /rcx :btrqImm8Reg [ 8 /rcx /rcx :movqMemDisp8Reg ] len :jcRel8 8 /rcx /rcx :movqMemDisp8Reg /rdx :popqReg 63 /rdx :btrqImm8Reg [ 8 /rdx /rdx :movqMemDisp8Reg ] len :jcRel8 8 /rdx /rdx :movqMemDisp8Reg /rcx /rdx :addqRegReg /rdx /rax :movqRegReg 32 /rax :shrqImm8Reg [ 63 /rdx :btsqImm8Reg /rdx :pushqReg 0 :jmpRel8 ] len :jnzRel8 63 /rdx :btsqImm8Reg /rdx :pushqReg [ /rdx :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg ] len :jmpRel8 /rdx :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg ] ] i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |and ::rawAddress eq }' { [ NATIVENOSCOPE [ /rcx :popqReg 63 /rcx :btrqImm8Reg [ 8 /rcx /rcx :movqMemDisp8Reg ] len :jcRel8 8 /rcx /rcx :movqMemDisp8Reg /rdx :popqReg 63 /rdx :btrqImm8Reg [ 8 /rdx /rdx :movqMemDisp8Reg ] len :jcRel8 8 /rdx /rdx :movqMemDisp8Reg 1 /rax :movqImmReg /rcx /rcx :testqRegReg [ /rax /rax :xorqRegReg ] len :jnzRel8 /rax /rax :xorqRegReg /rdx /rdx :testqRegReg [ /rax /rax :xorqRegReg ] len :jnzRel8 /rax /rax :xorqRegReg 63 /rax :btsqImm8Reg /rax :pushqReg ] ] i logic =[] [ NOP ] i 1 sub logic =[] } { ==negatedOpcodeName ==functionName { 1 last * functionName | ::rawAddress eq }' { [ NATIVENOSCOPE [ /rcx :popqReg 63 /rcx :btrqImm8Reg [ 8 /rcx /rcx :movqMemDisp8Reg ] len :jcRel8 8 /rcx /rcx :movqMemDisp8Reg /rdx :popqReg 63 /rdx :btrqImm8Reg [ 8 /rdx /rdx :movqMemDisp8Reg ] len :jcRel8 8 /rdx /rdx :movqMemDisp8Reg /rsi /rsi :xorqRegReg /rcx /rdx :cmpqRegReg [ /rsi :incqReg ] len negatedOpcodeName : /rsi :incqReg 63 /rsi :btsqImm8Reg /rsi :pushqReg ] ] i logic =[] [ NOP ] i 1 sub logic =[] } } /comparisonOperator deffst /eq /jnzRel8 comparisonOperator /neq /jzRel8 comparisonOperator /le /jnleRel8 comparisonOperator /lt /jnlRel8 comparisonOperator /ge /jngeRel8 comparisonOperator /gt /jngRel8 comparisonOperator ] conds } rep } each } /rewriteArithmetics deffst { _ ==logic 1 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last action CALL eq { 1 entry * "*" | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif { [ { 1 last * |and ::rawAddress eq }' { INLINEBLOCKAND i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |or ::rawAddress eq }' { INLINEBLOCKOR i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |not ::rawAddress eq }' { INLINEBLOCKNOT i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |eq ::rawAddress eq }' { INLINEBLOCKEQ i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |neq ::rawAddress eq }' { INLINEBLOCKNEQ i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |le ::rawAddress eq }' { INLINEBLOCKLE i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |lt ::rawAddress eq }' { INLINEBLOCKLT i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |ge ::rawAddress eq }' { INLINEBLOCKGE i logic =[] [ NOP ] i 1 sub logic =[] } { 1 last * |gt ::rawAddress eq }' { INLINEBLOCKGT i logic =[] [ NOP ] i 1 sub logic =[] } ] conds } rep } each } /rewriteArithmeticsOptimistic deffst { ==logic [ ] ==knownStack { [ knownStack _ len dearray ==ret ] =knownStack ret # TODO: make a real array pop } /knownPop deffst [ ] ==scopeReads [ ] ==stackReads [ ] ==scopeWrites 0 ==actualImprovement 0 ==consumedInputStack /NOREGISTER ==NOREGISTER 0 ==i 0 ==traceStart { ==why # "Restarting optimization attempt: " why cat dump # HINTS 0 =actualImprovement 0 =consumedInputStack [ ] =knownStack [ ] =scopeWrites [ ] =scopeReads [ ] =stackReads i 1 add =traceStart } =*restartTracing 0 ==aborted { ==why # "Aborting optimization attempt: " why cat dump # HINTS 1 =aborted } =*abortTracing { ==addrOrInt addrOrInt %8000000000000000 band { 1 } { addrOrInt ::rawObject sys .typed .type 0 eq } ? * } /isIntValue deffst { ==addrOrInt addrOrInt %8000000000000000 band { addrOrInt %00000000FFFFFFFF band } { addrOrInt ::rawObject } ? * } /getIntValue deffst { knownStack len { knownPop }' { [ /INPUT consumedInputStack _ 1 add =consumedInputStack NOREGISTER ] ==read stackReads [ read ] cat =stackReads read }' ? * } /consume deffst { knownStack len { knownPop -- }' { consumedInputStack 1 add =consumedInputStack }' ? * } /consumeAndDrop deffst { i logic len lt } { i logic * =*entry 0 entry ==action # "Known Stack: " dump # knownStack dump # "Handling: " dump # HINTS # |entry dump # HINTS [ { action NOP eq } { } { action PUSH eq } { knownStack [ |entry ] cat =knownStack } { action STATICTYPED eq { 4 entry sys .typed .type 0 eq }' andif } { 1 neg ==priorWrite 0 ==i { i scopeWrites len lt } { i scopeWrites * =*write 0 write STATICWRITE neq { |write dump "scopeWrites contains unexpected element type" die } rep 1 write 1 entry eq { 2 write 2 entry eq }' andif { 3 write 3 entry eq }' andif { i =priorWrite } rep i 1 add =i } loop priorWrite 0 ge { 4 priorWrite scopeWrites * * ==source knownStack [ source ] cat =knownStack } { 1 neg ==priorRead 0 ==i { i scopeReads len lt } { i scopeReads * =*read 0 read STATICTYPED neq { |read dump "scopeReads contains unexpected element type" die } rep 1 read 1 entry eq { 2 read 2 entry eq }' andif { 3 read 3 entry eq }' andif { i =priorRead } rep i 1 add =i } loop priorRead 0 ge { priorRead scopeReads * ==read knownStack [ read ] cat =knownStack } { [ |entry _ len dearray NOREGISTER ] ==read knownStack [ read ] cat =knownStack scopeReads [ read ] cat =scopeReads } ? * } ? * } { action STATICWRITE eq } { consume ==value 1 neg ==priorWrite 0 ==i { i scopeWrites len lt } { i scopeWrites * =*write 0 write STATICWRITE neq { |write dump "scopeWrites contains unexpected element type" die } rep 1 write 1 entry eq { 2 write 2 entry eq }' andif { 3 write 3 entry eq }' andif { i =priorWrite } rep i 1 add =i } loop priorWrite 0 ge { [ |entry _ len dearray value ] priorWrite scopeWrites =[] } { scopeWrites [ [ |entry _ len dearray value ] ] cat =scopeWrites } ? * } { action CALL eq { 1 entry "*" | ::rawCodeAddress eq }' andif { knownStack len }' andif } { knownPop ==top 0 top * PUSH eq { 1 top * ==called [ { called |eq ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /EQ left right NOREGISTER ] ] cat =knownStack } { called |neq ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /NEQ left right NOREGISTER ] ] cat =knownStack } { called |le ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /LE left right NOREGISTER ] ] cat =knownStack } { called |ge ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /GE left right NOREGISTER ] ] cat =knownStack } { called |lt ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /LT left right NOREGISTER ] ] cat =knownStack } { called |gt ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /GT left right NOREGISTER ] ] cat =knownStack } { called |or ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /OR left right NOREGISTER ] ] cat =knownStack } { called |and ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /LOGICFUNCTION /AND left right NOREGISTER ] ] cat =knownStack } { called |add ::rawAddress eq }' { consume ==right consume ==left knownStack [ [ /ARITHFUNCTION /ADD left right NOREGISTER ] ] cat =knownStack } { 1 } { "Unhandled called function" restartTracing # called dump HINTS } ] conds } { "Star on non-constant object" restartTracing } ? * } { action CALL eq { 1 entry "_" | ::rawCodeAddress eq }' andif } { consume ==value knownStack [ value value ] cat =knownStack } { action CALL eq { 1 entry "--" | ::rawCodeAddress eq }' andif } { consumeAndDrop } { action CALL eq { 1 entry "-" | ::rawCodeAddress eq }' andif { knownStack len }' andif } { knownPop ==shufflePush 0 shufflePush * PUSH eq { 1 shufflePush * ::rawObject ==shuffle 0 ==largestNumber 0 ==starUsed shuffle { _ 0 "*" * eq { 1 =starUsed } rep 0 "0" * sub largestNumber max =largestNumber } each starUsed { # shuffle dump # HINTS "Star used in stack shuffle" restartTracing } { [ 0 largestNumber 1 add range { -- consume } each ] =*shuffleInputs knownStack [ shuffle { 0 "0" * sub shuffleInputs } each ] cat =knownStack } ? * } { # shufflePush dump # HINTS "Non-constant stack shuffle" restartTracing } ? * } { 1 } { "Unhandled logic entry type" restartTracing # |entry dump # HINTS } ] conds i 1 add =i } loop 0 ==registerUsed [ /rcx /rdx /rbx /rsi /rdi ] ==availableRegisters # rbp excluded because SIB irregularities [ availableRegisters len { 0 } rep ] ==usedRegisters { 0 ==i /NONE ==reg { i availableRegisters len lt } { i usedRegisters * not { i availableRegisters * =reg 1 i usedRegisters =[] availableRegisters len =i } rep i 1 add =i } loop reg /NONE eq { "Out of registers" abortTracing /rax =reg } rep reg } /nextRegister deffst { ==reg 0 ==i { i availableRegisters len lt } { i availableRegisters * reg eq { 0 i usedRegisters =[] } rep i 1 add =i } loop } /freeRegister deffst < /al ==rax /bl ==rbx /cl ==rcx /dl ==rdx /spl ==rsp /bpl ==rbp /sil ==rsi /dil ==rdi > ==lowByte [ ] ==inputsChecked [ ] ==scopeReadsChecked [ ] ==traceConditions [ consumedInputStack { consumedInputStack 8 mul /rsp :addqImm8Reg } { } ? * ] ==traceCode aborted not { { =*entry [ { 0 entry /INPUT eq } { 2 } { 0 entry /LOGICFUNCTION eq } { 4 } { 0 entry /ARITHFUNCTION eq } { 4 } { 0 entry STATICTYPED eq } { 5 } { 1 } { 0 } ] conds } /hasRegister deffst { ==entry entry hasRegister _ { entry * } { "getRegister, but it has none" die } ? * } /getRegister deffst { =*entry [ { 0 entry /LOGICFUNCTION eq } { 0 } { 1 } { 1 } ] conds } /canProduceLargeInts deffst { =*entry [ { 0 entry /LOGICFUNCTION eq } { 0 } { 1 } { 1 } ] conds } /canProduceNonBooleans deffst { =*entry # "Compiling..." dump # HINTS # |entry dump { } ==?registerDeallocations # shall be returned [ { |entry hasRegister { |entry getRegister NOREGISTER neq }' andif } { } # entry already compiled { 0 entry PUSH eq } { } # don't put constant into a register { 0 entry /INPUT eq } { nextRegister _ ==target 2 |entry =[] { target freeRegister NOREGISTER 2 |entry =[] } registerDeallocations ; =registerDeallocations traceConditions [ 1 entry 8 mul /rsp target :movqMemDisp8Reg 63 target :btrqImm8Reg [ %F0 7 target :testbImmMemDisp8 0 :jnzRel8 8 target target :movqMemDisp8Reg 0 :jmpRel8 /rax /rax :xorqRegReg ] len :jcRel8 %F0 7 target :testbImmMemDisp8 [ 8 target target :movqMemDisp8Reg 0 :jmpRel8 ] len :jnzRel8 8 target target :movqMemDisp8Reg [ /rax /rax :xorqRegReg ] len :jmpRel8 /rax /rax :xorqRegReg ] cat =traceConditions } { 0 entry STATICWRITE eq } { 4 entry compileExpression registerDeallocations ; =registerDeallocations # reuse source register for ourselves (a bit) } { 0 entry STATICTYPED eq } { nextRegister _ ==target 5 |entry =[] { target freeRegister NOREGISTER 5 |entry =[] } registerDeallocations ; =registerDeallocations traceCode [ 2 entry { 16 /r14 target :movqMemDisp8Reg 2 entry 1 sub { 16 target target :movqMemDisp8Reg } rep 3 entry { 24 target target :movqMemDisp8Reg # load extension area pointer 1 entry target target :movqMemDisp32Reg } { 1 entry target target :movqMemDisp32Reg } ? * } { 3 entry { 24 /r14 target :movqMemDisp8Reg # load extension area pointer 1 entry target target :movqMemDisp32Reg } { 1 entry /r14 target :movqMemDisp32Reg } ? * } ? * 63 target :btrqImm8Reg [ 8 target target :movqMemDisp8Reg ] len :jcRel8 8 target target :movqMemDisp8Reg ] cat =traceCode } { ==tag ==setInstruction { 0 entry /LOGICFUNCTION eq { 1 entry tag eq }' andif } { 2 entry _ =*left compileExpression =*?deallocLeft 3 entry _ =*right compileExpression =*?deallocRight nextRegister _ ==target 4 |entry =[] { target freeRegister NOREGISTER 4 |entry =[] } registerDeallocations ; =registerDeallocations [ { 0 right PUSH eq { 1 right isIntValue }' andif { 1 right getIntValue %7F le }' andif } { [ { |left hasRegister } { |left getRegister ==reg 1 =actualImprovement traceCode [ target target :xorqRegReg 1 right getIntValue reg :cmpqImm8Reg lowByte target . setInstruction : ] cat =traceCode } { 1 } { tag ": unhandled left type" cat abortTracing } ] conds } { 1 } { tag ": unhandled right type" cat abortTracing } ] conds deallocLeft deallocRight } } /comparisonFunction deffst /seteReg /EQ comparisonFunction /setneReg /NEQ comparisonFunction /setleReg /LE comparisonFunction /setgeReg /GE comparisonFunction /setlReg /LT comparisonFunction /setgReg /GT comparisonFunction { ==tag ==combineInstruction { 0 entry /LOGICFUNCTION eq { 1 entry tag eq }' andif } { 2 entry _ =*left compileExpression =*?deallocLeft 3 entry _ =*right compileExpression =*?deallocRight nextRegister _ ==target 4 |entry =[] { target freeRegister NOREGISTER 4 |entry =[] } registerDeallocations ; =registerDeallocations [ { |right hasRegister } { |right getRegister ==rreg [ { |left hasRegister } { |left getRegister ==lreg 1 =actualImprovement |left canProduceNonBooleans { traceCode [ target target :xorqRegReg lreg lreg :testqRegReg lowByte target . :setneReg rreg rreg :testqRegReg /al :setneReg /al lowByte target . combineInstruction : ] cat =traceCode } { |right canProduceNonBooleans { traceCode [ lreg target :movqRegReg rreg rreg :testqRegReg /al :setneReg /al lowByte target . combineInstruction : ] cat =traceCode } { traceCode [ lreg target :movqRegReg lowByte rreg . lowByte target . combineInstruction : ] cat =traceCode } ? * } ? * } { 1 } { tag ": unhandled left type" cat abortTracing } ] conds } { 1 } { tag ": unhandled right type" cat abortTracing } ] conds deallocLeft deallocRight } } /logicFunction deffst /orbRegReg /OR logicFunction /andbRegReg /AND logicFunction { 0 entry /ARITHFUNCTION eq { 1 entry /ADD eq }' andif } { 2 entry _ =*left compileExpression =*?deallocLeft 3 entry _ =*right compileExpression =*?deallocRight nextRegister _ ==target 4 |entry =[] { target freeRegister NOREGISTER 4 |entry =[] } registerDeallocations ; =registerDeallocations [ { 0 right PUSH eq { 1 right isIntValue }' andif { 1 right getIntValue %7F le }' andif } { [ { |left hasRegister } { |left getRegister ==reg 1 =actualImprovement traceCode [ 1 right getIntValue reg target :leaqMemDisp8Reg ] cat =traceCode } { 1 } { "GT: unhandled left type" abortTracing } ] conds } { |right hasRegister } { |right getRegister ==rreg [ { |left hasRegister } { |left getRegister ==lreg 1 =actualImprovement traceCode [ lreg rreg target :leaqMemIndexReg ] cat =traceCode } { 1 } { "ADD: unhandled left type" abortTracing } ] conds } { 1 } { "ADD: unhandled right type" abortTracing } ] conds deallocLeft deallocRight } { 1 } { |entry dump "Invalid entry while compiling trace code" die } ] conds registerDeallocations } /compileExpression deffst scopeReads { compileExpression -- } each # do not deallocate, keep the values in registers to permit later writes stackReads { compileExpression -- } each # do not deallocate, keep the cleaned values in registers scopeWrites { _ =*entry compileExpression =*?registerDeallocations [ { 0 entry STATICWRITE eq } { 4 entry =*source NOREGISTER ==reg nextRegister ==tmpreg [ { 0 source PUSH eq } { /rax =reg traceCode [ 1 source tmpreg :movqImmReg ] cat =traceCode } { |source hasRegister } { |source getRegister =reg |source canProduceLargeInts { traceCode [ reg /rax :movqRegReg reg tmpreg :movqRegReg 32 /rax :shrqImm8Reg [ 63 tmpreg :btsqImm8Reg 0 :jmpRel8 ] len :jnzRel8 63 tmpreg :btsqImm8Reg [ availableRegisters { :pushqReg } each ::internalAllocateInteger /rax :movqImmReg /rax :callqReg availableRegisters reverse { :popqReg } each reg 8 /rax :movqRegMemDisp8 /rax tmpreg :movqRegReg ] len :jmpRel8 availableRegisters { :pushqReg } each ::internalAllocateInteger /rax :movqImmReg /rax :callqReg availableRegisters reverse { :popqReg } each reg 8 /rax :movqRegMemDisp8 /rax tmpreg :movqRegReg ] cat =traceCode } { traceCode [ reg tmpreg :movqRegReg 63 tmpreg :btsqImm8Reg ] cat =traceCode } ? * } { 1 } { |source dump "Invalid value source in scopeWrites" die } ] conds 2 entry { traceCode [ 16 /r14 /rax :movqMemDisp8Reg 2 entry 1 sub { 16 /rax /rax :movqMemDisp8Reg } rep ] cat =traceCode 3 entry { traceCode [ 24 /rax /rax :movqMemDisp8Reg # load extension area pointer tmpreg 1 entry /rax :movqRegMemDisp32 # write entry ] cat =traceCode } { traceCode [ tmpreg 1 entry /rax :movqRegMemDisp32 ] cat =traceCode } ? * } { 3 entry { traceCode [ 24 /r14 /rax :movqMemDisp8Reg # load extension area pointer tmpreg 1 entry /rax :movqRegMemDisp32 # write entry ] cat =traceCode } { traceCode [ tmpreg 1 entry /r14 :movqRegMemDisp32 ] cat =traceCode } ? * } ? * tmpreg freeRegister } { 1 } { "Invalid entry type in scopeWrites" die } ] conds registerDeallocations } each knownStack { _ =*entry compileExpression =*?registerDeallocations [ { 0 entry PUSH eq } { traceCode [ 1 entry /rax :movqImmReg /rax :pushqReg ] cat =traceCode } { |entry hasRegister } { |entry getRegister ==reg |entry canProduceLargeInts { traceCode [ reg /rax :movqRegReg 32 /rax :shrqImm8Reg [ reg /rax :movqRegReg # TODO: if no other output uses the register, this move can be removed 63 /rax :btsqImm8Reg /rax :pushqReg 0 :jmpRel8 ] len :jnzRel8 reg /rax :movqRegReg # TODO: if no other output uses the register, this move can be removed 63 /rax :btsqImm8Reg /rax :pushqReg [ availableRegisters { :pushqReg } each ::internalAllocateInteger /rax :movqImmReg /rax :callqReg availableRegisters reverse { :popqReg } each reg 8 /rax :movqRegMemDisp8 /rax :pushqReg ] len :jmpRel8 availableRegisters { :pushqReg } each ::internalAllocateInteger /rax :movqImmReg /rax :callqReg availableRegisters reverse { :popqReg } each reg 8 /rax :movqRegMemDisp8 /rax :pushqReg ] cat =traceCode } { traceCode [ reg /rax :movqRegReg # TODO: if no other output uses the register, this move can be removed 63 /rax :btsqImm8Reg /rax :pushqReg ] cat =traceCode } ? * } { 1 } { |entry dump "Invalid entry while assembling trace code" die } ] conds registerDeallocations } each } rep aborted not actualImprovement and { # "Improved integer trace" dump # HINTS # scopeReads dump # scopeWrites dump # knownStack dump [ 0 traceStart range { logic * } each [ CONDITIONALTAIL traceConditions traceCode ] traceConditions len 0 neq { # if the integer trace is always triggered, don't bother encoding the other case traceStart logic len range { logic * } each } rep ] =logic } rep logic } /rewriteIntegerTrace deffst { _ ==logic 1 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last [ { action CALL eq { 1 entry * "*" | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif }' { 1 last * ::rawObject ==executedObject executedObject sys .typed .type ==type type [ { last dump "failed to optimize 'execution' of integer typed object" die } { [ STRINGSTAR ] i logic =[] } { "failed to optimize 'execution' of float typed object" die } { "objects of type 3 should not appear" die } { "failed to optimize 'execution' of extension area" die } { # TODO think about handling typed functions executedObject sys .typed .inputs len { } { # handle untyped function executedObject sys .capturedScope { ==scope [ NOP ] i 1 sub logic =[] [ CALLSCOPED executedObject ::rawCodeAddress scope ::rawAddress ] i logic =[] executedObject ::rawCodeAddress 16 sub ::rawObject protectReference } { [ NOP ] i 1 sub logic =[] [ CALL executedObject ::rawCodeAddress ] i logic =[] executedObject ::rawCodeAddress 16 sub ::rawObject protectReference } ? * } ? * } { "failed to optimize 'execution' of raw function opcodes object" die } { [ ARRAYSTAR ] i logic =[] } { "failed to optimize 'execution' of function type descriptor" die } { } # TODO optimize scope execution one day { "failed to optimize 'execution' of name table" die } { "objects of type 11 should not appear" die } { } # TODO optimize coroutine execution one day { "objects of type 13 should not appear" die } { "objects of type 14 should not appear" die } { "objects of type 15 should not appear" die } ] * * } ] conds } each } /rewriteTrivialStar deffst { _ ==logic 1 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last [ { action CALL eq { 1 entry * "rep" | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif }' { 1 last * ::rawObject ==executedObject executedObject sys .typed .type ==type type [ { last dump "failed to optimize 'execution' of integer typed object" die } { } # TODO let's not optimize repeated string executions for now { "failed to optimize 'execution' of float typed object" die } { "objects of type 3 should not appear" die } { "failed to optimize 'execution' of extension area" die } { # TODO think about handling typed functions executedObject sys .typed .inputs len { } { # handle untyped function executedObject sys .capturedScope { -- # ignore concrete scope # [ UNTYPEDSCOPEDREP ] i logic =[] # TODO: /TODO_UNTYPEDSCOPEDREP_needed dump # also handle const, but scoped function objects } { [ NOP ] i 1 sub logic =[] [ RAWCONSTREP executedObject ::rawCodeAddress ] i logic =[] executedObject ::rawCodeAddress 16 sub ::rawObject protectReference } ? * } ? * } { "failed to optimize 'execution' of raw function opcodes object" die } { } # TODO let's not optimize repeated array executions for now { "failed to optimize 'execution' of function type descriptor" die } { } # TODO optimize scope execution one day { "failed to optimize 'execution' of name table" die } { "objects of type 11 should not appear" die } { } # TODO optimize coroutine execution one day { "objects of type 13 should not appear" die } { "objects of type 14 should not appear" die } { "objects of type 15 should not appear" die } ] * * } { action CALL eq { 1 entry * "rep" | ::rawCodeAddress eq }' andif { 0 last * FUNCTIONCREATE eq }' andif }' { 2 last * ==functionBody # the captured scope of the function is the same one rep is called in, no need to change scope [ NOP ] i 1 sub logic =[] [ RAWCONSTREPTHISSCOPE functionBody 16 add ] i logic =[] } ] conds } each } /rewriteTrivialRep deffst { _ ==logic 1 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last [ # FIXME handle constant cases, too { action CALL eq { 1 entry * "each" | ::rawCodeAddress eq }' andif { 0 last * FUNCTIONCREATE eq }' andif }' { # FIXME consider detecting the containter type iterated over and specialize 2 last * ==functionBody # the captured scope of the function is the same one each is called in, no need to change scope [ NOP ] i 1 sub logic =[] [ RAWCONSTEACHTHISSCOPE functionBody 16 add 1 last * ] i logic =[] } ] conds } each } /rewriteTrivialEach deffst { _ ==logic 2 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last i 2 sub logic * ==secondLast [ # FIXME handle constant cases, too { action CALL eq { 1 entry * "loop" | ::rawCodeAddress eq }' andif { 0 last * FUNCTIONCREATE eq }' andif { 0 secondLast * FUNCTIONCREATE eq }' andif }' { 2 last * ==loopBody 2 secondLast * ==headerBody # the captured scope of the function is the same one loop is called in, no need to change scope [ NOP ] i 2 sub logic =[] [ NOP ] i 1 sub logic =[] [ RAWCONSTLOOPTHISSCOPE headerBody 16 add loopBody 16 add ] i logic =[] } ] conds } each } /rewriteTrivialLoop deffst { _ ==logic 2 logic len range { ==i i logic * ==entry 0 entry * ==action [ # FIXME handle constant cases, too { action CALLSCOPED eq { 1 entry * "conds" | ::rawCodeAddress eq }' andif # { 0 last * FUNCTIONCREATE eq }' andif }' { [ ] ==collectedFunctions i 1 sub logic * ==arrayclose 0 ==arrayStart 0 arrayclose * CALL eq { 1 arrayclose * "]" | ::rawCodeAddress eq }' andif ==canRewrite i 2 sub ==j { canRewrite { j 0 ge }' andif }' { j logic * ==item [ { 0 item * CALL eq { 1 item * "[" | ::rawCodeAddress eq }' andif }' { j =arrayStart 1 neg =j }' # array start reached { 0 item * FUNCTIONCREATE eq }' { [ item ] collectedFunctions cat =collectedFunctions }' { 1 }' { 1 neg =j 1 neg =arrayStart } # something fancy is happening ] conds j 1 sub =j } loop arrayStart 0 ge canRewrite and =canRewrite # the captured scope of the function is the same one conds is called in, no need to change scope canRewrite { arrayStart i range { ==j [ NOP ] j logic =[] } each [ RAWCONSTCONDSTHISSCOPE collectedFunctions ] i logic =[] }' rep } ] conds } each } /rewriteTrivialConds deffst { _ ==logic 1 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last [ { action CALL eq { 1 entry * "*" | ::rawCodeAddress eq }' andif { 0 last * STATICTYPED eq }' andif }' { 4 last * ==executedObject executedObject sys .typed .type ==type type [ { last dump "failed to optimize 'execution' of integer typed object" die } { [ STRINGSTAR ] i logic =[] } { "failed to optimize 'execution' of float typed object" die } { "objects of type 3 should not appear" die } { "failed to optimize 'execution' of extension area" die } { # TODO think about handling typed functions executedObject sys .typed .inputs len { } { # handle untyped function executedObject sys .capturedScope { -- # ignore concrete scope [ UNTYPEDSCOPEDSTAR ] i logic =[] } { [ UNTYPEDUNSCOPEDSTAR ] i logic =[] } ? * } ? * } { "failed to optimize 'execution' of raw function opcodes object" die } { [ ARRAYSTAR ] i logic =[] } { "failed to optimize 'execution' of function type descriptor" die } { } # TODO optimize scope execution one day { "failed to optimize 'execution' of name table" die } { "objects of type 11 should not appear" die } { } # TODO optimize coroutine execution one day { "objects of type 13 should not appear" die } { "objects of type 14 should not appear" die } { "objects of type 15 should not appear" die } ] * * } ] conds } each } /rewriteConstantStar deffst { _ ==logic 3 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last i 2 sub logic * ==secondLast i 3 sub logic * ==thirdLast [ { action CALL eq { 1 entry * "*" | ::rawCodeAddress eq }' andif { 0 last * CALL eq }' andif { 1 last * "?" | ::rawCodeAddress eq }' andif { 0 secondLast * FUNCTIONCREATE eq }' andif { 0 thirdLast * FUNCTIONCREATE eq }' andif }' { [ NOP ] i 3 sub logic =[] [ NOP ] i 2 sub logic =[] [ NOP ] i 1 sub logic =[] [ INLINEQUESTIONSTARTHISSCOPE 2 thirdLast * 16 add 2 secondLast * 16 add ] i logic =[] } ] conds } each } /rewriteInlineQuestionStar deffst { _ ==logic 3 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last i 2 sub logic * ==secondLast i 3 sub logic * ==thirdLast [ { action CALL eq { 1 entry * "*" | ::rawCodeAddress eq }' andif { 0 last * CALL eq }' andif { 1 last * "?" | ::rawCodeAddress eq }' andif { 0 secondLast * PUSH eq }' andif { 0 thirdLast * PUSH eq }' andif { 1 secondLast * ::rawObject sys .typed .type 5 eq }' andif { 1 thirdLast * ::rawObject sys .typed .type 5 eq }' andif { 1 secondLast * ::rawObject sys .typed .inputs len not }' andif # TODO: think about typed functions { 1 thirdLast * ::rawObject sys .typed .inputs len not }' andif # TODO: think about typed functions }' { 1 secondLast * ::rawObject sys .capturedScope _ { -01 -- } { } ? * ==hasScope 1 thirdLast * ::rawObject sys .capturedScope _ { -01 -- } { } ? * hasScope or =hasScope hasScope { [ UNTYPEDSCOPEDSTAR ] i logic =[] } { [ NOP ] i 3 sub logic =[] [ NOP ] i 2 sub logic =[] [ NOP ] i 1 sub logic =[] [ INLINEQUESTIONSTARUNSCOPEDCONST 1 thirdLast * ::rawObject ::rawCodeAddress 1 secondLast * ::rawObject ::rawCodeAddress ] i logic =[] 1 thirdLast * ::rawObject ::rawCodeAddress 16 sub ::rawObject protectReference 1 secondLast * ::rawObject ::rawCodeAddress 16 sub ::rawObject protectReference } ? * } ] conds } each } /rewriteConstantQuestionStar deffst { _ ==logic 1 logic len range { ==i i logic * ==entry 0 entry * ==action i 1 sub logic * ==last [ { action CALL eq { 1 entry * "--" | ::rawCodeAddress eq }' andif { 0 last * CALL eq }' andif { 1 last * "]" | ::rawCodeAddress eq }' andif }' { [ NOP ] i 1 sub logic =[] [ ARRAYCLEAR ] i logic =[] } ] conds } each } /rewriteArrayClear deffst { ==logic [ NOP ] ==last [ logic { ==entry 0 entry * ==action [ { action CALL eq { 1 entry * "?" | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [ /rcx :popqReg /rdx :popqReg /rax :popqReg 63 /rax :btrqImm8Reg [ 8 /rax /rax :movqMemDisp8Reg ] len :jcRel8 8 /rax /rax :movqMemDisp8Reg /rax /rax :testqRegReg /rcx /rdx :cmovzqRegReg /rdx :pushqReg ] ] =entry } { action CALL eq { 1 entry * "_" | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [ 0 /rsp :pushqMemDisp8 ] ] =entry } { action CALL eq { 1 entry * "--" | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [ 8 /rsp :addqImm8Reg ] ] =entry } { action CALL eq { 1 entry * "-" | ::rawCodeAddress eq }' andif { 0 last * PUSH eq }' andif }' { 1 last * ::rawObject ==constant 0 ==largestNumber 0 ==starUsed constant { _ 0 "*" * eq { 1 =starUsed } rep 0 "0" * sub largestNumber max =largestNumber } each [ /rax /rcx /rdx /rbx /rbp /rsi /rdi ] =*availableRegisters starUsed { # TODO maybe implement this one day } { largestNumber |availableRegisters len gt { # TODO maybe implement this one day } { [ NATIVENOSCOPE [ 0 largestNumber 1 add range { availableRegisters :popqReg } each constant { 0 "0" * sub availableRegisters :pushqReg } each ] ] =entry [ NOP ] =last } ? * } ? * } { action CALL eq { 1 entry * "[" | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [ ARRAYMARKER :pushqImm32 ] ] =entry } { action CALL eq { 1 entry * "," | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [ POSITIONMARKER :pushqImm32 ] ] =entry } { action CALL eq { 1 entry * ",--" | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [[ /rsp /rsi :movqRegReg POSITIONMARKER /rax :movqImmReg /rdx :popqReg /rax /rdx :cmpqRegReg /done :jzLbl8 @loop 8 /rsi :addqImm8Reg /rdx /rsi :xchgqRegMem /rax /rdx :cmpqRegReg /loop :jnzLbl8 @done ]] ] =entry } { action CALL eq { 1 entry * "--," | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [[ /rsp /rsi :movqRegReg POSITIONMARKER /rax :movqImmReg /rdx :popqReg /rax /rdx :cmpqRegReg /done :jzLbl8 @loop 8 /rsi :addqImm8Reg /rdx /rsi :xchgqRegMem /rax /rdx :cmpqRegReg /loop :jnzLbl8 @done /rdx 8 /rsi :movqRegMemDisp8 ]] ] =entry } { action CALL eq { 1 entry * "_," | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [[ /rsp /rdi :movqRegReg POSITIONMARKER /rax :movqImmReg /rcx /rcx :xorqRegReg /rcx :decqReg :repnz :scasq /rdi :pushqMem ]] ] =entry } { action CALL eq { 1 entry * ",_" | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [[ /rsp /rdi :movqRegReg POSITIONMARKER /rax :movqImmReg /rcx /rcx :xorqRegReg /rcx :decqReg :repnz :scasq 16 neg /rdi :pushqMemDisp8 ]] ] =entry } { action CALL eq { 1 entry * ",---" | ::rawCodeAddress eq }' andif }' { [ NATIVENOSCOPE [[ POSITIONMARKER /rax :movqImmReg @loop /rdx :popqReg /rax /rdx :cmpqRegReg /loop :jnzLbl8 ]] ] =entry } ] conds last entry =last } each last ] } /rewriteSimpleFunctions deffst { ==someCodeAddress someCodeAddress peek * %48 eq { someCodeAddress 1 add peek * %b8 eq }' andif { someCodeAddress 10 add peek * %ff eq }' andif { someCodeAddress 11 add peek * %e0 eq }' andif { [ someCodeAddress 2 add _ 8 add range peek each ] 256 math .unbase forwardJumpPad } { someCodeAddress } ? * } /forwardJumpPad deffst o ::rawAddress ==addr [ addr _ 4 add range peek each ] 256 math .unbase ==totalLength [ addr 8 add _ 4 add range peek each ] 256 math .unbase ==codeLength addr 16 add ==i { =*ops =*set =*get ==pattern 1 ==found get ==j pattern { _ j ops -01 { eq not { 0 =found }' rep }' { -- -- }' ? * j 1 add =j }' each found _ { j set }' { }' ? * } /generalMatch deffst { { i }' { =i }' peek generalMatch }' /match deffst generalHeaderPattern match not { "failure while matching generalHeaderPattern" die }' rep scopingHeaderPattern match ==isScoping 1 ==continueParsing [ ] ==newLogic { newLogic [ -102 ] cat =newLogic }' /emitLogic deffst { continueParsing }' { i ==s [ { footerPattern match }' { 0 =continueParsing } { pushConstantPattern match }' { [ s 2 add _ 8 add range peek each ] 256 math .unbase ==pushedConstant [ PUSH pushedConstant ] emitLogic pushedConstant ::rawObject protectReference } { callConstantPattern match }' { [ s 2 add _ 8 add range peek each ] 256 math .unbase ==calledAddress calledAddress ==j calledAddress 16 sub ::rawObject protectReference { { j }' { =j }' peek generalMatch }' /callTargetMatch deffst [ { calledAddress 105553116266496 lt }' { # HEAPBASE # FIXME: should use global constant [ CALL calledAddress ] emitLogic } { constantActiveGeneralPattern callTargetMatch }' { [ calledAddress 3 add _ 8 add range peek each ] 256 math .unbase ==calledConstant [ PUSH calledConstant ] emitLogic [ CALL "*" | ::rawCodeAddress ] emitLogic calledConstant 16 sub ::rawObject protectReference } { constantNormalFunctionScopedUntypedPattern callTargetMatch }' { [ calledAddress 13 add _ 8 add range peek each ] 256 math .unbase ==functionScope [ calledAddress 23 add _ 8 add range peek each ] 256 math .unbase ==finalAddress [ CALLSCOPED finalAddress functionScope ] emitLogic finalAddress 16 sub ::rawObject protectReference functionScope ::rawObject protectReference } { constantNormalFunctionUnscopedUntypedPattern callTargetMatch }' { [ calledAddress 2 add _ 8 add range peek each ] 256 math .unbase ==finalAddress [ CALL finalAddress ] emitLogic finalAddress 16 sub ::rawObject protectReference } { constantPassivePattern callTargetMatch }' { [ calledAddress 3 add _ 8 add range peek each ] 256 math .unbase ==pushedConstant [ PUSH pushedConstant ] emitLogic pushedConstant ::rawObject protectReference } { staticLoadPattern callTargetMatch }' { 0 ==parentCount { staticLoadParentPattern callTargetMatch }' { parentCount 1 add =parentCount } loop j ==loadStart { ==addrOrInt # FIXME: why does replacing ::rawObject below with this thing make ugly errors? addrOrInt %8000000000000000 band { addrOrInt %00000000FFFFFFFF band } { addrOrInt ::rawObject } ? * } /ensureBoxed deffst [ { staticLoadFromScopePattern callTargetMatch }' { [ loadStart 4 add _ 4 add range peek each ] 256 math .unbase ==offsetInScope [ calledAddress 8 sub _ 8 add range peek each ] 256 math .unbase ==exampleObjectOffset [ calledAddress exampleObjectOffset add _ 8 add range peek each ] 256 math .unbase ::rawObject ==exampleObject [ STATICTYPED offsetInScope parentCount 0 exampleObject ] emitLogic exampleObject protectReference } { staticLoadFromExtensionPattern callTargetMatch }' { [ loadStart 8 add _ 4 add range peek each ] 256 math .unbase ==offsetInExtensionArea [ calledAddress 8 sub _ 8 add range peek each ] 256 math .unbase ==exampleObjectOffset [ calledAddress exampleObjectOffset add _ 8 add range peek each ] 256 math .unbase ::rawObject ==exampleObject [ STATICTYPED offsetInExtensionArea parentCount 1 exampleObject ] emitLogic exampleObject protectReference } { 1 }' { [ j j 16 add range peek each ] dump o dump j dump "unparsed static load opcodes in sys .opt .hook (optimizing version)" die } ] conds [ { staticLoadActivePattern callTargetMatch }' { [ CALL "*" | ::rawCodeAddress ] emitLogic } { staticLoadPassivePattern callTargetMatch }' { # nothing to emit } { 1 }' { [ j j 16 add range peek each ] dump o dump j dump "unparsed static load opcodes in sys .opt .hook (optimizing version)" die } ] conds } { customFunctionConstantPassivePattern callTargetMatch }' { [ calledAddress 9 add _ 8 add range peek each ] 256 math .unbase ==pushedConstant [ PUSH pushedConstant ] emitLogic pushedConstant ::rawObject protectReference } { customFunctionObjectCreationHeaderPattern callTargetMatch }' { [ calledAddress 9 add _ 8 add range peek each ] 256 math .unbase ==functionBody # function code object address [ FUNCTIONCREATE calledAddress functionBody ] emitLogic functionBody ::rawObject protectReference } { customFunctionObjectCreationHeaderPatternPatched callTargetMatch }' { [ calledAddress 9 add _ 8 add range peek each ] 256 math .unbase ==functionBody # function code object address [ FUNCTIONCREATE calledAddress functionBody ] emitLogic functionBody ::rawObject protectReference } { customFunctionHeaderPattern callTargetMatch }' { [ CALL calledAddress ] emitLogic } { 1 }' { [ j j 16 add range peek each ] dump o dump j dump "unparsed call target opcodes in sys .opt .hook (optimizing version)" die } ] conds } { 1 }' { [ i i 16 add range peek each ] dump o dump i dump "unparsed opcodes in sys .opt .hook (optimizing version)" die } ] conds } loop [ ] ==newOpcodes { newOpcodes -01 cat =newOpcodes } /emitOpcodes deffst [ ] ==newReferences { ==ref [ { ref 105553116266496 lt } { } # HEAPBASE # FIXME: use a global constant { ref 123145302310912 ge } { } # %700000000000 { 1 } { newReferences [ ref ::rawObject ] cat =newReferences } ] conds } /emitReference deffst # [ :ud2 ] emitOpcodes # enable for further development newLogic testNametableTemplatability testScopeModifications rewriteSlash containsScopeModifications not { rewriteConstantPipe rewriteConstantAssignment } rep rewriteConstantDot rewriteTrivialStar rewriteTrivialRep rewriteTrivialEach rewriteTrivialLoop rewriteTrivialConds rewriteConstantStar rewriteInlineQuestionStar rewriteConstantQuestionStar rewriteArrayClear rewriteIntegerTrace rewriteArithmetics # mostly SUPERSEDED BY rewriteIntegerTrace rewriteArithmeticsOptimistic rewriteSimpleFunctions testAllocatedScopeEscape # _ dump # enable this line to see intermediate representation isScoping { allocatedScopeMightEscape { [ 8 neg /r15 :popqMemDisp8 16 /r15 :subqImm8Reg /r14 /r15 :movqRegMem /r14 /rsi :movqRegReg INITIALSCOPESIZE /rdi :movqImmReg ::internalAllocateScope /rax :movqImmReg /rax :callqReg /rax /r14 :movqRegReg ] emitOpcodes }' { # allocate scope directly on the call stack # this works even though we cannot GC the scope object anymore, because # the stack contents are tracked anyway [ 8 neg /r15 :popqMemDisp8 16 /r15 :subqImm8Reg /r14 /r15 :movqRegMem INITIALSCOPESIZE 8 mul 32 add /r15 :subqImm8Reg /rax /rax :xorqRegReg /rax 0 /r15 :andqRegMemDisp8 /rax 8 /r15 :andqRegMemDisp8 /r14 16 /r15 :movqRegMemDisp8 # save parent scope /rax 24 /r15 :andqRegMemDisp8 0 INITIALSCOPESIZE range { ==i /rax i 8 mul 32 add /r15 :andqRegMemDisp8 } each INITIALSCOPESIZE 8 mul 32 add /r15 :orbImmMem # set length %96 7 /r15 :orbImmMemDisp8 # set type and existence of all pointers /r15 /r14 :movqRegReg ] emitOpcodes }' ? * templateNametable { [ templateNametable /rax :movqImmReg /rax 8 /r14 :movqRegMemDisp8 ] emitOpcodes templateNametable emitReference } { } ? * }' { [ 8 neg /r15 :popqMemDisp8 8 /r15 :subqImm8Reg ] emitOpcodes }' ? * isScoping { allocatedScopeMightEscape { [ /r15 /r14 :movqMemReg 16 /r15 :addqImm8Reg 8 neg /r15 :jmpqMemDisp8 ] }' { [ INITIALSCOPESIZE 8 mul 48 add /r15 :addqImm8Reg 16 neg /r15 /r14 :movqMemDisp8Reg 8 neg /r15 :jmpqMemDisp8 ] }' ? * }' { [ /r15 :pushqMem 8 /r15 :addqImm8Reg :retn ] }' ? * ==newFooter { =*entry 0 entry ==action [ { action PUSH eq }' { [ 1 entry /rax :movqImmReg /rax :pushqReg ] emitOpcodes 1 entry emitReference } { action CALL eq }' { [ 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg ] emitOpcodes 1 entry 16 sub emitReference } { action FUNCTIONCREATE eq }' { [ 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg ] emitOpcodes 1 entry 16 sub emitReference } { action CALLSCOPED eq }' { [ 8 /r15 :subqImm8Reg /r14 /r15 :movqRegMem 2 entry /r14 :movqImmReg 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg /r15 /r14 :movqMemReg 8 /r15 :addqImm8Reg ] emitOpcodes 1 entry 16 sub emitReference 2 entry emitReference } { action [ STATIC STATICTYPED ] eq any }' { [ 2 entry { 16 /r14 /rax :movqMemDisp8Reg 2 entry 1 sub { 16 /rax /rax :movqMemDisp8Reg } rep 3 entry { 24 /rax /rcx :movqMemDisp8Reg # load extension area pointer 1 entry /rcx :pushqMemDisp32 # push loaded entry to stack } { 1 entry /rax :pushqMemDisp32 } ? * } { 3 entry { 24 /r14 /rcx :movqMemDisp8Reg # load extension area pointer 1 entry /rcx :pushqMemDisp32 # push loaded entry to stack } { 1 entry /r14 :pushqMemDisp32 } ? * } ? * ] emitOpcodes } { action STATICWRITE eq }' { [ 2 entry { 16 /r14 /rax :movqMemDisp8Reg 2 entry 1 sub { 16 /rax /rax :movqMemDisp8Reg } rep 3 entry { 24 /rax /rcx :movqMemDisp8Reg # load extension area pointer 1 entry /rcx :popqMemDisp32 # load entry from stack } { 1 entry /rax :popqMemDisp32 } ? * } { 3 entry { 24 /r14 /rcx :movqMemDisp8Reg # load extension area pointer 1 entry /rcx :popqMemDisp32 # load entry from stack } { 1 entry /r14 :popqMemDisp32 } ? * } ? * ] emitOpcodes } { action [ STATICDOT STATICTYPEDDOT ] eq any }' { [ /rax :popqReg 2 entry { 16 /rax /rax :movqMemDisp8Reg } rep 3 entry { 24 /rax /rcx :movqMemDisp8Reg # load extension area pointer 1 entry /rcx :pushqMemDisp32 # push loaded entry to stack } { 1 entry /rax :pushqMemDisp32 } ? * ] emitOpcodes } { action UNTYPEDSCOPEDSTAR eq }' { [ /rax :popqReg 8 /r15 :subqImm8Reg /r14 /r15 :movqRegMem 8 /rax /r14 :movqMemDisp8Reg 24 /rax /rax :movqMemDisp8Reg 16 /rax :addqImm8Reg /rax :callqReg /r15 /r14 :movqMemReg 8 /r15 :addqImm8Reg ] emitOpcodes } { action UNTYPEDUNSCOPEDSTAR eq }' { [ /rax :popqReg 24 /rax /rax :movqMemDisp8Reg 16 /rax :addqImm8Reg /rax :callqReg ] emitOpcodes } { action ARRAYSTAR eq }' { [[ /rsi :popqReg /rax :popqReg 63 /rax :btrqImm8Reg /unboxedInteger :jcLbl8 %f0 7 /rax :andbImmMemDisp8 /boxedInteger :jzLbl8 /rax :pushqReg /rsi :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg /done :jmpLbl8 @boxedInteger 8 /rax /rax :movqMemDisp8Reg @unboxedInteger /rsi /ecx :movlMemReg 3 /rcx :shrqImm8Reg /rcx :decqReg :cqo /rcx :idivqReg 0 /rdx :cmpqImm8Reg [ /rcx /rdx :addqRegReg ] len :jgeRel8 /rcx /rdx :addqRegReg 8 8 /rdx /rsi :pushqMemIndexScaleDisp8 @done ]] emitOpcodes } { action STRINGSTAR eq }' { [[ /rsi :popqReg /rax :popqReg 63 /rax :btrqImm8Reg /unboxedInteger :jcLbl8 %f0 7 /rax :andbImmMemDisp8 /boxedInteger :jzLbl8 /rax :pushqReg /rsi :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg /done :jmpLbl8 @boxedInteger 8 /rax /rax :movqMemDisp8Reg @unboxedInteger 16 /rsi /rcx :movqMemDisp8Reg :cqo /rcx :idivqReg 0 /rdx :cmpqImm8Reg [ /rcx /rdx :addqRegReg ] len :jgeRel8 /rcx /rdx :addqRegReg 24 1 /rdx /rsi /rbx :movzxMem8IndexScaleDisp8Reg64 63 /rbx :btsqImm8Reg /rbx :pushqReg @done ]] emitOpcodes } { action [ NATIVE NATIVENOSCOPE ] eq any }' { 1 entry emitOpcodes } { action NOP eq }' { } { action CONDITIONALTAIL eq }' { 1 entry len { [ 1 /rax :movqImmReg ] emitOpcodes 1 entry emitOpcodes [ /rax /rax :testqRegReg 2 entry len newFooter len add :jzRel32 ] emitOpcodes 2 entry emitOpcodes newFooter emitOpcodes } { 2 entry emitOpcodes # test would always succeed, footer will follow immediately } ? * } { action ARRAYCLEAR eq }' { [[ ARRAYMARKER /rax :movqImmReg @search /rdi :popqReg /rdi /rax :cmpqRegReg /search :jneLbl8 ]] emitOpcodes } { action RAWCONSTREP eq }' { [[ 8 /r15 :subqImm8Reg /rax :popqReg 63 /rax :btrqImm8Reg /unboxedInt :jcLbl8 8 /rax /rax :movqMemDisp8Reg @unboxedInt /rax /r15 :movqRegMem @repLoop 0 /r15 :cmpqImm8Mem /repFinished :jzLbl8 1 /r15 :subqImm8Mem 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg /repLoop :jmpLbl8 @repFinished 8 /r15 :addqImm8Reg ]] emitOpcodes 1 entry 16 sub emitReference } { action RAWCONSTREPTHISSCOPE eq }' { [[ 16 /r15 :subqImm8Reg /r14 8 /r15 :movqRegMemDisp8 /rax :popqReg 63 /rax :btrqImm8Reg /unboxedInt :jcLbl8 8 /rax /rax :movqMemDisp8Reg @unboxedInt /rax /r15 :movqRegMem @repLoop 0 /r15 :cmpqImm8Mem /repFinished :jzLbl8 1 /r15 :subqImm8Mem 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg 8 /r15 /r14 :movqMemDisp8Reg /repLoop :jmpLbl8 @repFinished 16 /r15 :addqImm8Reg ]] emitOpcodes 1 entry 16 sub emitReference } { action RAWCONSTEACHTHISSCOPE eq }' { [[ 32 /r15 :subqImm8Reg /rax :popqReg 7 /rax /cl :movbMemDisp8Reg %F0 /cl :andbImmReg %70 /cl :cmpbImmReg /eachArray :jzLbl8 %10 /cl :cmpbImmReg /eachString :jzLbl8 # revert to original code for complicated (e.g. scope) cases /rax :pushqReg 2 entry forwardJumpPad /rax :movqImmReg /rax :callqReg |each ::rawCodeAddress /rax :movqImmReg /rax :callqReg /end :jmpLbl32 @eachArray /rax 24 /r15 :movqRegMemDisp8 8 /rax /rcx :leaqMemDisp8Reg /rcx /r15 :movqRegMem /rax /edx :movlMemReg 1 /rdx /rax /rcx :leaqMemIndexScaleReg /rcx 8 /r15 :movqRegMemDisp8 # /r15 -> current array element # 8 /r15 -> address after last array element # 16 /r15 -> current scope # 24 /r15 -> array object (to keep the GC away) /r14 16 /r15 :movqRegMemDisp8 @loop 16 /r15 /r14 :movqMemDisp8Reg /r15 /rax :movqMemReg 8 /r15 /rax :cmpqMemDisp8Reg /end :jnbLbl8 /rax :pushqMem # push array element 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg 8 /r15 :addqImm8Mem /loop :jmpLbl8 @eachString /rax 24 /r15 :movqRegMemDisp8 16 /rax /rcx :movqMemDisp8Reg /rcx /rcx :testqRegReg /end :jzLbl8 /rcx 8 /r15 :movqRegMemDisp8 24 /rax /rcx :leaqMemDisp8Reg /rcx /r15 :movqRegMem # /r15 -> current string element # 8 /r15 -> count remaining # 16 /r15 -> current scope # 24 /r15 -> string object (to keep the GC away) /r14 16 /r15 :movqRegMemDisp8 @stringLoop /r15 /rdx :movqMemReg /rdx /rdx :movzxMem8Reg64 63 /rdx :btsqImm8Reg /rdx :pushqReg 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg 16 /r15 /r14 :movqMemDisp8Reg 1 /r15 :addqImm8Mem 1 8 /r15 :subqImm8MemDisp8 /stringLoop :jnzLbl8 @end 32 /r15 :addqImm8Reg ]] emitOpcodes 1 entry 16 sub emitReference 2 entry 16 sub emitReference } { action RAWCONSTLOOPTHISSCOPE eq }' { [[ 8 /r15 :subqImm8Reg /r14 /r15 :movqRegMem @loop 1 entry forwardJumpPad /rax :movqImmReg /rax :callqReg /r15 /r14 :movqMemReg /rax :popqReg 63 /rax :btrqImm8Reg /unboxedInt :jcLbl8 8 /rax /rax :movqMemDisp8Reg @unboxedInt /rax /rax :testqRegReg /done :jzLbl8 2 entry forwardJumpPad /rax :movqImmReg /rax :callqReg /r15 /r14 :movqMemReg /loop :jmpLbl8 @done 8 /r15 :addqImm8Reg ]] emitOpcodes 1 entry 16 sub emitReference 2 entry 16 sub emitReference } { action INLINEQUESTIONSTARTHISSCOPE eq }' { [[ /rax :popqReg 63 /rax :btrqImm8Reg /unboxedInt :jcLbl8 /rax :pushqReg # TODO: maybe handle boxed ints one day 1 entry /rax :movqImmReg /rax :pushqReg 2 entry /rax :movqImmReg /rax :pushqReg "?" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg /rdx :popqReg /callCreatedFunction :jmpLbl8 @unboxedInt 1 entry forwardJumpPad /rcx :movqImmReg 2 entry forwardJumpPad /rdx :movqImmReg /rax /rax :testqRegReg /rcx /rdx :cmovnzqRegReg @callCreatedFunction 8 /r15 :subqImm8Reg /r14 /r15 :movqRegMem /rdx :callqReg /r15 /r14 :movqMemReg 8 /r15 :addqImm8Reg ]] emitOpcodes 1 entry 16 sub emitReference 2 entry 16 sub emitReference } { action INLINEQUESTIONSTARUNSCOPEDCONST eq }' { [[ /rax :popqReg 63 /rax :btrqImm8Reg /unboxedInt :jcLbl8 /rax :pushqReg # TODO: maybe handle boxed ints one day 1 entry forwardJumpPad /rax :movqImmReg /rax :pushqReg 2 entry forwardJumpPad /rax :movqImmReg /rax :pushqReg "?" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg /rdx :popqReg /callCreatedFunction :jmpLbl8 @unboxedInt 1 entry forwardJumpPad /rcx :movqImmReg 2 entry forwardJumpPad /rdx :movqImmReg /rax /rax :testqRegReg /rcx /rdx :cmovnzqRegReg @callCreatedFunction /rdx :callqReg ]] emitOpcodes 1 entry 16 sub emitReference 2 entry 16 sub emitReference } { action RAWCONSTCONDSTHISSCOPE eq }' { 1 entry ==functions { =*function [ { 0 function FUNCTIONCREATE eq }' { 2 function 16 add forwardJumpPad /rax :movqImmReg /rax :callqReg /r15 /r14 :movqMemReg 2 function emitReference }' { 1 }' { |function dump "invalid entry while assembling RAWCONSTCONDSTHISSCOPE" die }' ] conds } /call deffst [[ 8 /r15 :subqImm8Reg /r14 /r15 :movqRegMem 0 ==i { i functions len lt }' { i txt .produce .u ==lbl i functions * call /rax :popqReg 63 /rax :btrqImm8Reg lbl "_unboxed" cat :jcLbl8 8 /rax /rax :movqMemDisp8Reg lbl "_unboxed" cat @ /rax /rax :testqRegReg lbl "_no" cat :jzLbl8 i 1 add functions * call /end :jmpLbl32 lbl "_no" cat @ i 2 add =i } loop @end 8 /r15 :addqImm8Reg ]] emitOpcodes } { 1 }' { |entry dump "invalid intermediate code during optimize" die } ] conds } each newFooter emitOpcodes newOpcodes newReferences o ::replace allocatedScopeMightEscape not { addr 7 add _ sys .asm .peek %02 bor -01 sys .asm .poke # set scope-cannot-escape bit [ addr 18 add _ 8 add range peek each ] 256 math .unbase ==targetAddress targetAddress 16 sub 7 add _ sys .asm .peek %02 bor -01 sys .asm .poke # set scope-cannot-escape bit on jump target } rep 1 executingScope # return something different from o to signal successful optimization [ ] _ =newOpcodes _ =newReferences =newLogic } /optimize deffd 0 ==recursionDepth { recursionDepth 1 add =recursionDepth recursionDepth 3 lt { optimize } { } ? * recursionDepth 1 sub =recursionDepth } /hook sys .opt .deff > -- # ensure that the optimizer is run often enough to finish optimize itself [ /eq /neq /le /lt /ge /gt /and /or /add ] { ==function 10 { 1 "{" sys .executeIdentifier "i" "==" sys .executeIdentifier "i" sys .executeIdentifier 1 function sys .executeIdentifier "}" sys .executeIdentifier * -- } rep function dump } each # vim: syn=elymas