aboutsummaryrefslogtreecommitdiff
path: root/compiler/elymasGlobalSysAsm.ey
blob: de787045e06acb952f09fb51de537c1cbfc71e71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
<
  <
    # 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