forked from froggey/Mezzano
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlap-x86.lisp
1812 lines (1634 loc) · 69.7 KB
/
lap-x86.lisp
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
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;;; x86-64 assembler.
(in-package :mezzano.lap.x86)
;; FIXME: This is not entirely correct... This table is modified when this
;; file is loaded, which can happen concurrently with reads. It is on
;; a hot path, so the risk seems worth it for now...
(defparameter *instruction-assemblers* (make-hash-table :synchronized nil :enforce-gc-invariant-keys t))
(defvar *cpu-mode* nil "The CPU mode to assemble for.")
(defvar *fixup-target*)
(defconstant +operand-size-override+ #x66
"The operand size override prefix.")
(defconstant +address-size-override+ #x67
"The address size override prefix.")
(defvar *following-immediate-bytes* 0
"Number of immediate bytes following an encoded effective address.
Used to make rip-relative addressing line up right.")
(defmethod mezzano.lap:perform-assembly-using-target ((target mezzano.compiler:x86-64-target) code-list &rest args &key (cpu-mode 64) &allow-other-keys)
(let ((*cpu-mode* cpu-mode))
(apply 'perform-assembly *instruction-assemblers* code-list args)))
(defmacro define-instruction (name lambda-list &body body)
(let ((insn (gensym)))
`(add-instruction ',name #'(lambda (,insn)
#+mezzano (declare (sys.int::lambda-name (instruction ,name)))
(destructuring-bind ,lambda-list (rest ,insn)
(block instruction
,@body
(error "Could not encode instruction ~S." ,insn)))))))
(defun find-x86-lap-definitions (name)
(let ((assembler (gethash name *instruction-assemblers*)))
(when assembler
(let ((loc (mezzano.debug:function-source-location assembler)))
(when loc
(list (list `(define-instruction ,name)
loc)))))))
(mezzano.extensions:add-find-definitions-hook 'find-x86-lap-definitions)
(defmacro define-macro-instruction (name lambda-list &body body)
(let ((insn (gensym)))
`(add-instruction ',name `(:macro ,#'(lambda (,insn)
#+mezzano (declare (sys.int::lambda-name (instruction ,name)))
(destructuring-bind ,lambda-list (rest ,insn)
(block ,name
,@body)))))))
(defun add-instruction (name function)
(export name '#:mezzano.lap.x86)
(setf (gethash name *instruction-assemblers*) function))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun concat-symbols (a b &optional (package *package*))
(intern (format nil "~A~A" (symbol-name a) (symbol-name b)) package)))
(defun reg-class (reg)
(case reg
((:rax :rcx :rdx :rbx :rsp :rbp :rsi :rdi :r8 :r9 :r10 :r11 :r12 :r13 :r14 :r15) :gpr-64)
((:eax :ecx :edx :ebx :esp :ebp :esi :edi :r8d :r9d :r10d :r11d :r12d :r13d :r14d :r15d) :gpr-32)
((:ax :cx :dx :bx :sp :bp :si :di :r8w :r9w :r10w :r11w :r12w :r13w :r14w :r15w) :gpr-16)
((:al :cl :dl :bl :spl :bpl :sil :dil :r8l :r9l :r10l :r11l :r12l :r13l :r14l :r15l :ah :ch :dh :bh) :gpr-8)
((:mm0 :mm1 :mm2 :mm3 :mm4 :mm5 :mm6 :mm7) :mm)
((:xmm0 :xmm1 :xmm2 :xmm3 :xmm4 :xmm5 :xmm6 :xmm7 :xmm8 :xmm9 :xmm10 :xmm11 :xmm12 :xmm13 :xmm14 :xmm15) :xmm)
((:cr0 :cr1 :cr2 :cr3 :cr4 :cr5 :cr6 :cr7) :cr)
((:dr0 :dr1 :dr2 :dr3 :dr4 :dr5 :dr6 :dr7) :dr)
((:cs :ss :ds :es :fs :gs) :segment)))
(defun reg-number (reg)
(ecase reg
((:rax :eax :ax :al :mm0 :xmm0 :cr0 :dr0 :es ) 0)
((:rcx :ecx :cx :cl :mm1 :xmm1 :cr1 :dr1 :cs ) 1)
((:rdx :edx :dx :dl :mm2 :xmm2 :cr2 :dr2 :ss ) 2)
((:rbx :ebx :bx :bl :mm3 :xmm3 :cr3 :dr3 :ds ) 3)
((:rsp :esp :sp :spl :mm4 :xmm4 :cr4 :dr4 :fs :ah ) 4)
((:rbp :ebp :bp :bpl :mm5 :xmm5 :cr5 :dr5 :gs :ch ) 5)
((:rsi :esi :si :sil :mm6 :xmm6 :cr6 :dr6 :dh ) 6)
((:rdi :edi :di :dil :mm7 :xmm7 :cr7 :dr7 :bh ) 7)
((:r8 :r8d :r8w :r8l :xmm8 ) 8)
((:r9 :r9d :r9w :r9l :xmm9 ) 9)
((:r10 :r10d :r10w :r10l :xmm10 ) 10)
((:r11 :r11d :r11w :r11l :xmm11 ) 11)
((:r12 :r12d :r12w :r12l :xmm12 ) 12)
((:r13 :r13d :r13w :r13l :xmm13 ) 13)
((:r14 :r14d :r14w :r14l :xmm14 ) 14)
((:r15 :r15d :r15w :r15l :xmm15 ) 15)))
(defun convert-width (reg width)
(dolist (conv '((:rax :eax :ax :al)
(:rcx :ecx :cx :cl)
(:rdx :edx :dx :dl)
(:rbx :ebx :bx :bl)
(:rsp :esp :sp :spl)
(:rbp :ebp :bp :bpl)
(:rsi :esi :si :sil)
(:rdi :edi :di :dil)
(:r8 :r8d :r8w :r8l)
(:r9 :r9d :r9w :r9l)
(:r10 :r10d :r10w :r10l)
(:r11 :r11d :r11w :r11l)
(:r12 :r12d :r12w :r12l)
(:r13 :r13d :r13w :r13l)
(:r14 :r14d :r14w :r14l)
(:r15 :r15d :r15w :r15l))
(error "Unknown register ~S." reg))
(when (member reg conv)
(return (ecase width
(8 (fourth conv))
(16 (third conv))
(32 (second conv))
(64 (first conv)))))))
(defun is-bp (reg) (find reg '(:bpl :bp :ebp :rbp)))
(defun is-sp (reg) (find reg '(:spl :sp :esp :rsp)))
(defun short-form-valid (class value)
(and (not (eql value :fixup))
(multiple-value-bind (umin umax)
(ecase class
(:gpr-64 (values #xffffffffffffff80 #xffffffffffffffff))
(:gpr-32 (values #xffffff80 #xffffffff))
(:gpr-16 (values #xff80 #xffff)))
(or (> 128 value -128) (>= umax value umin)))))
(defun maybe-emit-operand-size-override (class)
(ecase class
((:gpr-32 :gpr-64)
(when (= *cpu-mode* 16)
(emit +operand-size-override+)))
(:gpr-16
(unless (= *cpu-mode* 16)
(emit +operand-size-override+)))
(:gpr-8)))
(defun encode-register (reg)
"Return the register number and if a rex flag must be set."
(let ((nr (if (integerp reg) reg (reg-number reg))))
(if (>= nr 8)
(values (- nr 8) t)
(values nr nil))))
(defun encode-modrm (mod r/m reg)
"Combine seperate mod/rm/reg values into one modr/m byte"
(logior (ash mod 6) r/m (ash reg 3)))
(defun encode-sib (base index scale)
"Combine seperate base/index/scale values into one SIB byte"
(logior base (ash index 3) (ash scale 6)))
(defun encode-rex (&key w r x b)
(logior #b01000000
(if w #b1000 0)
(if r #b0100 0)
(if x #b0010 0)
(if b #b0001 0)))
(defun emit-rex (&key w r x b)
(emit (encode-rex :w w :r r :x x :b b)))
(defun register-conflicts-with-rex (reg)
(member reg '(:ah :bh :ch :dh)))
(defun register-requires-rex-p (reg)
(>= (reg-number reg) 8))
(defun extended-8-bit-register-p (reg)
(member reg '(:spl :bpl :sil :dil)))
(defun pick-operand-size (class)
(ecase class
(:gpr-64 64)
(:gpr-32 32)
(:gpr-16 16)
(:gpr-8 8)))
(defun encode-disp32 (displacement)
(check-type displacement (signed-byte 32))
(list (ldb (byte 8 0) displacement)
(ldb (byte 8 8) displacement)
(ldb (byte 8 16) displacement)
(ldb (byte 8 24) displacement)))
(defun emit-modrm-address (class opcode reg displacement base index scale
&key rex-w)
(let ((force-rex nil)
(operand-size-override nil)
(address-size-override nil)
(rex nil)
(encoded-modrm-sib '())
(need-disp32 nil))
(multiple-value-bind (reg-nr rex-r)
(encode-register reg)
(multiple-value-bind (base-nr rex-b)
(when base (encode-register base))
(multiple-value-bind (index-nr rex-x)
(when index (encode-register index))
(ecase class
((:xmm :mm))
(:gpr-64 (assert (= *cpu-mode* 64) (*cpu-mode*)
"64-bit operand-size only supported in 64-bit mode.")
(setf rex-w t))
(:gpr-32 (when (= *cpu-mode* 16)
(setf operand-size-override t)))
(:gpr-16 (when (/= *cpu-mode* 16)
(setf operand-size-override t)))
(:gpr-8 (when (extended-8-bit-register-p reg)
(setf force-rex t))))
;; Compute the address size
(let* ((base-class (when base (reg-class base)))
(index-class (when index (reg-class index)))
(address-class (or base-class index-class)))
(when (and base-class index-class
(not (eql base-class index-class)))
(error "Impossible addressing mode."))
(ecase address-class
(:gpr-64 (assert (= *cpu-mode* 64) (*cpu-mode*)
"64-bit address-size only supported in 64-bit mode."))
(:gpr-32 (ecase *cpu-mode*
((64 16) (setf address-size-override t))
(32 nil)))
(:gpr-16 (ecase *cpu-mode*
(64 (error "Cannot use 16-bit addressing modes in 64-bit mode."))
(32 (setf address-size-override t))
(16 nil)))
((nil) nil)))
;; Compute rex byte and check for conflicts
(when (or force-rex rex-w rex-r rex-b rex-x)
(when (register-conflicts-with-rex reg)
(error "Cannot encode ~S with REX prefix." reg))
(setf rex (encode-rex :w rex-w :r rex-r :b rex-b :x rex-x)))
;; Build ModR/M and SIB bytes
(if (and (null index) (null scale))
;; Encodings that only use base & displacement
(cond
((and (or (null displacement) (eql displacement 0))
base)
(cond ((= base-nr #b100)
;; Force SIB
(setf encoded-modrm-sib (list (encode-modrm #b00 #b100 reg-nr)
(encode-sib base-nr #b100 #b00))))
((= base-nr #b101)
;; Force displacement
(setf encoded-modrm-sib (list (encode-modrm #b01 base-nr reg-nr)
#x00)))
(t (setf encoded-modrm-sib (list (encode-modrm #b00 base-nr reg-nr))))))
((and displacement (not (eql displacement 0))
base)
(if (= base-nr #b100)
(cond ((typep displacement '(signed-byte 8))
(setf encoded-modrm-sib (list (encode-modrm #b01 #b100 reg-nr)
(encode-sib base-nr #b100 #b00)
(ldb (byte 8 0) displacement))))
(t
(setf encoded-modrm-sib (list (encode-modrm #b10 #b100 reg-nr)
(encode-sib base-nr #b100 #b00))
need-disp32 t)))
(cond ((typep displacement '(signed-byte 8))
(setf encoded-modrm-sib (list (encode-modrm #b01 base-nr reg-nr)
(ldb (byte 8 0) displacement))))
(t
(setf encoded-modrm-sib (list (encode-modrm #b10 base-nr reg-nr))
need-disp32 t)))))
((and (null base))
;; Avoid the rip-relative encoding when in 64-bit mode.
(if (and (not address-size-override)
(= *cpu-mode* 64))
(setf encoded-modrm-sib (list (encode-modrm #b00 #b100 reg-nr)
(encode-sib #b101 #b100 #b00)))
(setf encoded-modrm-sib (list (encode-modrm #b00 #b101 reg-nr))))
(setf need-disp32 t))
(t (error "Unknown/impossible addressing mode.")))
(let ((ss (ecase scale ((nil 1) #b00) (2 #b01) (4 #b10) (8 #b11))))
(when (null index)
(error "Scale without index."))
(when (is-sp index)
(error "Impossible index register ~S." index))
(cond
((null base)
(setf encoded-modrm-sib (list (encode-modrm #b00 #b100 reg-nr)
(encode-sib #b101 index-nr ss))
need-disp32 t))
((or (is-bp base)
(member base '(:r13 :r13d :r13w :r13l))
(and displacement (/= displacement 0)))
(cond ((typep displacement '(or null (signed-byte 8)))
(setf encoded-modrm-sib (list (encode-modrm #b01 #b100 reg-nr)
(encode-sib base-nr index-nr ss)
(ldb (byte 8 0) (or displacement 0)))))
(t
(setf encoded-modrm-sib (list (encode-modrm #b10 #b100 reg-nr)
(encode-sib base-nr index-nr ss))
need-disp32 t))))
((or (null displacement) (= displacement 0))
(setf encoded-modrm-sib (list (encode-modrm #b00 #b100 reg-nr)
(encode-sib base-nr index-nr ss))))
(t (error "Unknown/impossible addressing mode.")))))
(when operand-size-override
(emit +operand-size-override+))
(when address-size-override
(emit +address-size-override+))
(when rex (emit rex))
(if (listp opcode)
(apply #'emit opcode)
(emit opcode))
(apply #'emit encoded-modrm-sib)
(when need-disp32
;; Sometimes there's no displacement but one is needed anyway.
(when displacement
(emit-relocation :abs32le
displacement
0))
(emit 0 0 0 0)))))))
(defun emit-modrm-register (class opcode reg r/m-reg
&key rex-w)
(when (eql class :gpr-64)
(assert (= *cpu-mode* 64) (*cpu-mode*) "64-bit operand-size only supported in 64-bit mode."))
(let ((force-rex nil)
(operand-size-override nil)
(rex 'nil))
(multiple-value-bind (reg-nr rex-r)
(encode-register reg)
(multiple-value-bind (r/m-nr rex-b)
(encode-register r/m-reg)
(ecase class
((:xmm :mm))
(:gpr-64 (setf rex-w t))
(:gpr-32 (when (= *cpu-mode* 16)
(setf operand-size-override t)))
(:gpr-16 (when (/= *cpu-mode* 16)
(setf operand-size-override t)))
(:gpr-8 (when (extended-8-bit-register-p reg)
(setf force-rex t))
(when (extended-8-bit-register-p r/m-reg)
(setf force-rex t))))
(when (or force-rex rex-w rex-r rex-b)
(when (register-conflicts-with-rex reg)
(error "Cannot encode ~S with REX prefix." reg))
(when (register-conflicts-with-rex r/m-reg)
(error "Cannot encode ~S with REX prefix." r/m-reg))
(setf rex (encode-rex :w rex-w :r rex-r :b rex-b)))
(when operand-size-override
(emit +operand-size-override+))
(when rex (emit rex))
(if (listp opcode)
(apply #'emit opcode)
(emit opcode))
(emit (encode-modrm #b11 r/m-nr reg-nr))))))
(defun emit-modrm-rip-relative (class opcode reg displacement &key rex-w)
(assert (= *cpu-mode* 64) (*cpu-mode*) "RIP-relative addressing only supported in 64-bit mode.")
(let ((force-rex nil)
(operand-size-override nil)
(rex nil))
(multiple-value-bind (reg-nr rex-r)
(encode-register reg)
(ecase class
((:xmm :mm))
(:gpr-64 (setf rex-w t))
(:gpr-32 nil)
(:gpr-16 (setf operand-size-override t))
(:gpr-8 (when (extended-8-bit-register-p reg)
(setf force-rex t))))
(when (or force-rex rex-w rex-r)
(setf rex (encode-rex :w rex-w :r rex-r)))
(when operand-size-override
(emit +operand-size-override+))
(when rex (emit rex))
(if (listp opcode)
(apply #'emit opcode)
(emit opcode))
(emit (encode-modrm #b00 #b101 reg-nr))
(emit-relocation :rel32le
displacement
(- (+ *following-immediate-bytes* 4)))
(emit 0 0 0 0))))
(defun parse-object-ea (form segment slot-scale)
(destructuring-bind (base slot &optional index (scale 8) &key (offset 0))
(rest form)
(values nil
base
index
(if index scale nil)
;; subtract +tag-object+, skip object header.
;; Return an expression, so slot goes through symbol resolution, etc.
`(+ (- ,sys.int::+tag-object+) 8
,(if (eql slot-scale :location)
`(mezzano.runtime::location-offset ,slot)
`(* ,slot ,slot-scale))
,@(if offset (list offset) nil))
nil
segment)))
(defun memory-operand-p (form)
(and (consp form)
(not (immediatep form))))
(defun parse-r/m (form)
"Parse a register or effective address into a bunch of values.
First value is the register, or false if the expression is an effective address.
Remaining values describe the effective address: base index scale disp rip-relative segment"
(cond ((not (memory-operand-p form))
form)
((and (= (length form) 2)
(eql (first form) :constant))
;; Transform (:constant foo) into (:rip (:constant-address foo))
(values nil nil nil nil (list :constant-address (second form)) t))
((and (= (length form) 2)
(eql (first form) :function))
;; Transform (:function foo) into (:rip (:constant-address (fref foo)))
(values nil nil nil nil (list :constant-address (funcall mezzano.lap:*function-reference-resolver* (second form))) t))
((and (= (length form) 2)
(eql (first form) :symbol-global-cell))
;; Transform (:symbol-global-cell foo) into (:rip (:constant-address (fref foo)))
(values nil nil nil nil (list :constant-address (mezzano.runtime::symbol-global-value-cell (second form))) t))
((and (= (length form) 2)
(eql (first form) :stack)
(integerp (second form)))
;; Transform (:stack n) into (:rbp (- (* (1+ n) 8))).
(values nil :rbp nil nil (- (* (1+ (second form)) 8)) nil))
((and (= (length form) 2)
(keywordp (first form))
(not (reg-class (first form)))
(reg-class (second form)))
(ecase (first form)
(:car (values nil (second form) nil nil (- sys.int::+tag-cons+)))
(:cdr (values nil (second form) nil nil (+ (- sys.int::+tag-cons+) 8)))))
((and (member (first form) '(:cs :ss :ds :es :fs :gs))
(eql (second form) :object))
(parse-object-ea (rest form) (first form) 8))
((eql (first form) :object)
(parse-object-ea form nil 8))
((and (member (first form) '(:cs :ss :ds :es :fs :gs))
(eql (second form) :object-unscaled))
(parse-object-ea (rest form) (first form) 1))
((eql (first form) :object-unscaled)
(parse-object-ea form nil 1))
((and (member (first form) '(:cs :ss :ds :es :fs :gs))
(eql (second form) :object-location))
(parse-object-ea (rest form) (first form) :location))
((eql (first form) :object-location)
(parse-object-ea form nil :location))
(t (let (base index scale disp rip-relative segment)
(dolist (elt form)
(cond ((eql elt :rip)
(assert (null rip-relative) () "Multiple :RIP forms in r/m form ~S." form)
(assert (and (null base) (null index) (null scale)) ()
"RIP-relative addressing only supports displacements.")
(assert (= *cpu-mode* 64) () "RIP-relative addressing is only supported in 64-bit mode.")
(setf rip-relative t))
((member elt '(:cs :ss :ds :es :fs :gs))
(assert (null segment) () "Multiple segments in r/m form ~S." form)
(setf segment elt))
((reg-class elt)
(assert (null rip-relative) ()
"RIP-relative addressing only supports displacements.")
(cond ((not base) (setf base elt))
((not index) (setf index elt))
(t (error "Too many registers in r/m form ~S." form))))
((and (listp elt)
(eql (length elt) 2)
(or (eql (reg-class (first elt)) :gpr-16)
(eql (reg-class (first elt)) :gpr-32)
(eql (reg-class (first elt)) :gpr-64))
(integerp (second elt)))
(assert (null rip-relative) ()
"RIP-relative addressing only supports displacements.")
(assert (not (or index scale)) () "Too many index/scale values in r/m form ~S." form)
(setf index (first elt)
scale (second elt))
(when (and scale (not (integerp scale)))
(error "Scale value must be an integer in r/m form ~S." form)))
(t (assert (null disp) () "Multiple displacements in r/m form ~S." form)
(setf disp elt))))
(values nil base index scale disp rip-relative segment)))))
(defun generate-modrm (class r/m reg opc)
(multiple-value-bind (r/m-reg base index scale disp rip-relative segment)
(parse-r/m r/m)
(let ((disp-value (when disp (or (resolve-immediate disp) disp))))
(when segment
(emit (ecase segment
(:cs #x2E)
(:ss #x36)
(:ds #x3E)
(:es #x26)
(:fs #x64)
(:gs #x65))))
(cond
(r/m-reg
(emit-modrm-register class opc reg r/m-reg :rex-w (eql class :gpr-64)))
(rip-relative
(emit-modrm-rip-relative class opc reg disp-value :rex-w (eql class :gpr-64)))
(t
(emit-modrm-address class opc reg disp-value base index scale :rex-w (eql class :gpr-64))))
t)))
(defun generate-jmp (dest short-opc long-opc)
(note-variably-sized-instruction)
(let ((value (resolve-immediate dest)))
(when value
(setf value (- value *current-address* 2))
(cond ((<= -128 value 127)
(emit short-opc)
(emit (ldb (byte 8 0) value)))
((listp long-opc)
(apply #'emit long-opc)
(apply #'emit (encode-disp32 (- value 4))))
(t
(emit long-opc)
(apply #'emit (encode-disp32 (- value 3)))))
t)))
(defun emit-imm (width imm &optional (signedp t))
(unless (integerp width)
(setf width (ecase width
((:gpr-64 :gpr-32) 4)
(:gpr-16 2)
((:gpr-8 :mm :xmm) 1))))
(when (eql imm :fixup)
(assert (and (= width 4) signedp))
(note-fixup *fixup-target*)
(emit #xFF #xFF #xFF #xFF)
(return-from emit-imm))
(let ((limit (ecase width
(1 #x100)
(2 #x10000)
(4 #x100000000)
(8 #x10000000000000000))))
(unless (<= (- limit) imm (1- limit))
(error "Value out of bounds."))
(dotimes (i width)
(emit (ldb (byte 8 (* i 8)) imm)))))
(defun emit-imm-with-relocation (width imm &optional (signedp t))
(let ((value (resolve-immediate imm)))
(when value
(let ((*fixup-target* imm))
(return-from emit-imm-with-relocation
(emit-imm width value signedp)))))
(unless (integerp width)
(setf width (ecase width
((:gpr-64 :gpr-32) 4)
(:gpr-16 2)
((:gpr-8 :mm :xmm) 1))))
(emit-relocation (ecase width
(1 (if signedp :abs8 :absu8))
(2 (if signedp :abs16le :absu16le))
(4 (if signedp :abs32le :absu32le))
(8 (if signedp :abs64le :absu64le)))
imm
0)
(dotimes (i width)
(emit 0)))
(defun generate-imm-ax (class reg imm opc)
(declare (ignore reg))
(when (eql class :gpr-64)
(emit-rex :w t))
(maybe-emit-operand-size-override class)
(emit opc)
(emit-imm-with-relocation class imm)
t)
(defmacro modrm (class r/m reg opc)
`(when (and (eql ,class (reg-class ,reg))
(or (eql (reg-class ,r/m) ,class)
(memory-operand-p ,r/m))
,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't))
(return-from instruction
(generate-modrm ,class ,r/m ,reg ,opc))))
(defmacro modrm-single (class r/m opc opc-minor)
`(when ,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't)
(return-from instruction
(generate-modrm ,class ,r/m ,opc-minor ,opc))))
(defmacro modrm-imm8 (class r/m reg imm8 opc)
`(when (and (eql ,class (reg-class ,reg))
(or (eql (reg-class ,r/m) ,class)
(memory-operand-p ,r/m))
,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't)
(immediatep ,imm8))
(let ((*following-immediate-bytes* 1))
(generate-modrm ,class ,r/m ,reg ,opc))
(emit-imm-with-relocation 1 ,imm8)
(return-from instruction t)))
(defun generate-imm (class r/m imm opc opc-minor)
(let ((*following-immediate-bytes* (ecase class
((:gpr-64 :gpr-32) 4)
(:gpr-16 2)
((:gpr-8 :xmm :mm) 1))))
(generate-modrm class r/m opc-minor opc))
(emit-imm-with-relocation class imm)
t)
(defmacro imm (class dst src opc opc-minor)
`(when (and (not (reg-class ,src))
(immediatep ,src)
(eql ,class (or (reg-class ,dst) ,class))
,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't))
(return-from instruction
(let ((*fixup-target* ,src))
(generate-imm ,class ,dst ,src ,opc ,opc-minor)))))
(defun generate-imm-short (class r/m imm opc opc-minor)
(let ((*following-immediate-bytes* 1))
(generate-modrm class r/m opc-minor opc))
(emit-imm-with-relocation 1 imm)
t)
(defmacro imm-short (class dst imm opc opc-minor)
`(when (and (not (reg-class ,imm))
(immediatep ,imm)
(resolve-immediate ,imm)
(short-form-valid ,class (resolve-immediate ,imm))
(eql ,class (or (reg-class ,dst) ,class))
,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't))
(return-from instruction
(generate-imm-short ,class ,dst ,imm ,opc ,opc-minor))))
(defmacro imm-ax (class reg imm opc)
(let ((ax-reg (ecase class
(:gpr-64 :rax)
(:gpr-32 :eax)
(:gpr-16 :ax)
(:gpr-8 :al))))
`(when (and (not (reg-class ,imm))
(immediatep ,imm)
(eq ,reg ,ax-reg)
,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't))
(return-from instruction
(generate-imm-ax ,class ,reg ,imm ,opc)))))
(defun generate-shift-imm (class r/m amount 1-opc n-opc opc-minor)
(let ((value (resolve-immediate amount)))
(cond ((eql value 1)
(generate-modrm class r/m opc-minor 1-opc)
t)
(t
(let ((*following-immediate-bytes* 1))
(generate-modrm class r/m opc-minor n-opc))
(emit-imm-with-relocation 1 amount)
t))))
(defmacro shift-imm (class dst amount 1-opc n-opc opc-minor)
`(when (and (not (reg-class ,amount))
(immediatep ,amount)
(eql ,class (or (reg-class ,dst) ,class))
,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't))
(return-from instruction
(generate-shift-imm ,class ,dst ,amount ,1-opc ,n-opc ,opc-minor))))
(defun generate-big-shift-imm (class r/m reg amount opc)
(let ((*following-immediate-bytes* 1))
(generate-modrm class r/m reg opc))
(emit-imm-with-relocation 1 amount)
t)
(defmacro big-shift-imm (class dst src amount opc)
`(when (and (not (reg-class ,amount))
(immediatep ,amount)
(eql ,class (or (reg-class ,dst) ,class))
,(if (eql class :gpr-64) '(= *cpu-mode* 64) 't))
(return-from instruction
(generate-big-shift-imm ,class ,dst ,src ,amount ,opc))))
(defmacro jmp-imm (dst short-opc long-opc)
`(when (and (not (reg-class ,dst))
(immediatep ,dst))
(return-from instruction
(generate-jmp ,dst ,short-opc ,long-opc))))
(defmacro define-simple-instruction (name opc)
`(define-instruction ,name ()
,@(if (listp opc)
(mapcar (lambda (x) `(emit ,x)) opc)
(list `(emit ,opc)))
(return-from instruction t)))
(define-instruction !code16 ()
(setf *cpu-mode* 16)
(return-from instruction t))
(define-instruction !code32 ()
(setf *cpu-mode* 32)
(return-from instruction t))
(define-instruction !code64 ()
(setf *cpu-mode* 64)
(return-from instruction t))
;;; Prefixes, not real instructions.
(define-simple-instruction repne #xF2)
(define-simple-instruction repnz #xF2)
(define-simple-instruction rep #xF3)
(define-simple-instruction cs #x2E)
(define-simple-instruction ss #x36)
(define-simple-instruction ds #x3E)
(define-simple-instruction es #x26)
(define-simple-instruction fs #x64)
(define-simple-instruction gs #x65)
(define-simple-instruction hint-not-taken #x2E)
(define-simple-instruction hint-taken #x3E)
(define-simple-instruction operand-size-override #x66)
(define-simple-instruction address-size-override #x67)
(define-simple-instruction fwait #x9B)
(define-simple-instruction pushf #x9C)
(define-simple-instruction popf #x9D)
(define-simple-instruction sahf #x9E)
(define-simple-instruction lahf #x9F)
(define-simple-instruction ret #xC3)
(define-simple-instruction leave #xC9)
(define-simple-instruction hlt #xF4)
(define-simple-instruction cmc #xF5)
(define-simple-instruction clc #xF8)
(define-simple-instruction stc #xF9)
(define-simple-instruction cli #xFA)
(define-simple-instruction sti #xFB)
(define-simple-instruction cld #xFC)
(define-simple-instruction std #xFD)
(define-simple-instruction syscall (#x0F #x05))
(define-simple-instruction clts (#x0F #x06))
(define-simple-instruction sysret (#x0F #x07))
(define-simple-instruction invd (#x0F #x08))
(define-simple-instruction wbinvd (#x0F #x09))
(define-simple-instruction ud2 (#x0F #x0B))
(define-simple-instruction wrmsr (#x0F #x30))
(define-simple-instruction rdtsc (#x0F #x31))
(define-simple-instruction rdmsr (#x0F #x32))
(define-simple-instruction rdpmc (#x0F #x33))
(define-simple-instruction sysenter (#x0F #x34))
(define-simple-instruction sysexit (#x0F #x35))
(define-simple-instruction getsec (#x0F #x37))
(define-simple-instruction emms (#x0F #x77))
(define-simple-instruction cpuid (#x0F #xA2))
(define-simple-instruction rsm (#x0F #xAA))
(define-simple-instruction pause (#xF3 #x90))
(define-simple-instruction nop #x90)
(define-simple-instruction fninit (#xDB #xE3))
(define-simple-instruction lfence (#x0F #xAE #xE8))
(define-simple-instruction mfence (#x0F #xAE #xF8))
(define-simple-instruction sfence (#x0F #xAE #xF0))
(define-instruction lock (&rest extra)
(emit #xF0)
(when extra
(funcall (or (gethash (first extra) *instruction-assemblers*)
(error "Unknown instruction ~S" (first extra)))
extra))
(return-from instruction t))
(defmacro define-integer-define-instruction (name lambda-list (bitness class) &body body)
`(defmacro ,name ,lambda-list
`(progn ,@(mapcar (lambda (,bitness ,class) ,@body)
'(8 16 32 64)
'(:gpr-8 :gpr-16 :gpr-32 :gpr-64)))))
(define-integer-define-instruction define-arithmetic-instruction (name n) (bitness class)
(let ((width-flag (if (= bitness 8) 0 1))
(opc (ash n 3)))
`(define-instruction ,(intern (format nil "~A~D" (symbol-name name) bitness)) (dst src)
(modrm ,class dst src ,(logior opc width-flag))
(modrm ,class src dst ,(logior opc width-flag 2))
,@(unless (eql bitness 8)
(list `(imm-short ,class dst src #x83 ,n)))
(imm-ax ,class dst src ,(logior opc 4 width-flag))
(imm ,class dst src ,(logior #x80 width-flag) ,n))))
(define-arithmetic-instruction add 0)
(define-arithmetic-instruction or 1)
(define-arithmetic-instruction adc 2)
(define-arithmetic-instruction sbb 3)
(define-arithmetic-instruction and 4)
(define-arithmetic-instruction sub 5)
(define-arithmetic-instruction xor 6)
(define-arithmetic-instruction cmp 7)
(defmacro define-conditional-instruction (name lambda-list (condition-bits) &body body)
(flet ((frob (cond bits)
`(define-instruction ,(concat-symbols name cond) ,lambda-list
(let ((,condition-bits ,bits))
,@body))))
(let ((conditions '(o no (b nae c) (nb ae nc) (e z) (ne nz) (be na) (nbe a)
s ns (p pe) (np po) (l nge) (nl ge) (le ng) (nle g)))
(i 0))
(list* 'progn (mapcar #'(lambda (cond)
(prog1
(if (symbolp cond)
(frob cond i)
(list* 'progn (mapcar (lambda (cond) (frob cond i)) cond)))
(incf i)))
conditions)))))
(define-conditional-instruction j (dst) (condition-bits)
(jmp-imm dst (logior #x70 condition-bits) (list #x0F (logior #x80 condition-bits))))
(defmacro named-call (dst opcode)
`(when (and (consp ,dst)
(eql (first ,dst) :named-call))
(let ((fref (funcall mezzano.lap:*function-reference-resolver* (second ,dst))))
;; Named direct jump to an FREF.
;; Make sure to add the FREF to the constant pool so the GC is aware
(mezzano.lap:add-to-constant-pool fref)
(emit ',opcode)
(note-fixup fref)
(emit #xFF #xFF #xFF #xFF)
(return-from instruction t))))
(define-instruction jmp (dst)
(named-call dst #xE9)
(jmp-imm dst #xEB #xE9)
(when (= *cpu-mode* 16)
(modrm-single :gpr-16 dst #xff 4))
(when (= *cpu-mode* 32)
(modrm-single :gpr-32 dst #xff 4))
(when (= *cpu-mode* 64)
(modrm-single :gpr-64 dst #xff 4)))
(define-conditional-instruction set (dst) (condition-bits)
(modrm-single :gpr-8 dst (list #x0F (logior #x90 condition-bits)) 0))
;;; The generated names are bit off here. cmov64ne instead of cmovne64. :(
(define-conditional-instruction cmov16 (dst src) (condition-bits)
(modrm :gpr-16 src dst (list #x0F (logior #x40 condition-bits))))
(define-conditional-instruction cmov32 (dst src) (condition-bits)
(modrm :gpr-32 src dst (list #x0F (logior #x40 condition-bits))))
(define-conditional-instruction cmov64 (dst src) (condition-bits)
(modrm :gpr-64 src dst (list #x0F (logior #x40 condition-bits))))
(define-integer-define-instruction define-shift-instruction (name n) (bitness class)
(let ((width-flag (if (= bitness 8) 0 1)))
`(define-instruction ,(intern (format nil "~A~D" (symbol-name name) bitness)) (dst amount)
(shift-imm ,class dst amount ,(logior #xD0 width-flag) ,(logior #xC0 width-flag) ,n)
(when (member amount '(:cl :cx :ecx :rcx))
(modrm-single ,class dst ,(logior #xD2 width-flag) ,n)))))
(define-shift-instruction rol 0)
(define-shift-instruction ror 1)
(define-shift-instruction rcl 2)
(define-shift-instruction rcr 3)
(define-shift-instruction shl 4)
(define-shift-instruction shr 5)
(define-shift-instruction sar 7)
(define-integer-define-instruction define-unary-integer-instruction (name n) (bitness class)
(let ((width-flag (if (= bitness 8) 0 1)))
`(define-instruction ,(intern (format nil "~A~D" (symbol-name name) bitness)) (loc)
(modrm-single ,class loc ,(logior #xF6 width-flag) ,n))))
(define-unary-integer-instruction not 2)
(define-unary-integer-instruction neg 3)
(defmacro define-simple-instruction-with-operand-size (name opc &optional (valid-classes '(:gpr-8 :gpr-16 :gpr-32 :gpr-64)))
(list* 'progn (mapcar (lambda (class)
`(define-instruction ,(if (= (length valid-classes) 1)
name
(intern (format nil "~A~D" (symbol-name name)
(ecase class
(:gpr-8 8)
(:gpr-16 16)
(:gpr-32 32)
(:gpr-64 64)))))
()
,@(ecase class
(:gpr-8 nil)
((:gpr-16 :gpr-32)
(list `(maybe-emit-operand-size-override ,class)))
(:gpr-64
(list `(emit-rex :w t))))
(emit ,(logior opc (if (eql class :gpr-8)
0
1)))
(return-from instruction t)))
valid-classes)))
(define-simple-instruction-with-operand-size movs #xA4)
(define-simple-instruction-with-operand-size cmps #xA6)
(define-simple-instruction-with-operand-size stos #xAA)
(define-simple-instruction-with-operand-size lods #xAC)
(define-simple-instruction-with-operand-size scas #xAE)
(define-simple-instruction-with-operand-size cwd #x99 (:gpr-16))
(define-simple-instruction-with-operand-size cdq #x99 (:gpr-32))
(define-simple-instruction-with-operand-size cqo #x99 (:gpr-64))
(defmacro define-integer-instruction (name lambda-list (class) &body body)
(list* 'progn
(mapcar (lambda (the-class bitness)
`(define-instruction ,(intern (format nil "~A~D" (symbol-name name) bitness))
,lambda-list
(let ((,class ,the-class))
,@body)))
'(:gpr-8 :gpr-16 :gpr-32 :gpr-64)
'(8 16 32 64))))
(define-integer-instruction test (dst src) (class)
(let ((width-bit (if (eql class :gpr-8) 0 1)))
(modrm class dst src (logior #x84 width-bit))
(imm class dst src (logior #xF6 width-bit) 0)))
(define-integer-instruction xchg (lhs rhs) (class)
(let ((width-bit (if (eql class :gpr-8) 0 1)))
(modrm class lhs rhs (logior #x86 width-bit))))
(define-integer-instruction xadd (lhs rhs) (class)
(let ((width-bit (if (eql class :gpr-8) 0 1)))
(modrm class lhs rhs `(#x0F ,(logior #xC1 width-bit)))))
(define-integer-instruction shld (dst src count) (class)
(when (eql count :cl)
(modrm class dst src '(#x0F #xA5)))
(big-shift-imm class dst src count '(#x0F #xA4)))
(define-integer-instruction shrd (dst src count) (class)
(when (eql count :cl)
(modrm class dst src '(#x0F #xAD)))
(big-shift-imm class dst src count '(#x0F #xAC)))
(define-integer-instruction mov (dst src) (class)
(let ((width-bit (if (eql class :gpr-8) 0 1)))
(when (and (eql class :gpr-64)
(not (reg-class src))
(immediatep src)
(= *cpu-mode* 64)
(eql (reg-class dst) :gpr-64))
(let ((value (resolve-immediate src)))
(unless (eql value :fixup)
(return-from instruction
(multiple-value-bind (nr rex-b)
(encode-register dst)
(emit-rex :w t :b rex-b)
(emit (+ #xB8 nr))
(emit-imm-with-relocation 8 value)
t)))))
(modrm class dst src (logior #x88 width-bit))
(modrm class src dst (logior #x8A width-bit))
(unless (eql class :gpr-64)
(when (and (immediatep src)
(eql (reg-class dst) class))
(return-from instruction
(multiple-value-bind (nr rex-b)
(encode-register dst)
(maybe-emit-operand-size-override class)
(when rex-b
(when (register-conflicts-with-rex dst)
(error "Cannot encode ~S with REX prefix." dst))
(emit-rex :b rex-b))
(emit (+ (if (eql class :gpr-8) #xB0 #xB8) nr))
(emit-imm-with-relocation class src)
t))))
(imm class dst src (logior #xC6 width-bit) 0)))
(define-integer-instruction out (port) (class)
(unless (eql class :gpr-64)
(when (eql :dx port)
(maybe-emit-operand-size-override class)
(emit (logior #xEE (if (eql class :gpr-8) 0 1)))
(return-from instruction t))
(when (immediatep port)
(maybe-emit-operand-size-override class)
(emit (logior #xE6 (if (eql class :gpr-8) 0 1)))
(emit-imm-with-relocation 1 port)
(return-from instruction t))))
(define-integer-instruction in (port) (class)
(unless (eql class :gpr-64)
(when (eql :dx port)
(maybe-emit-operand-size-override class)
(emit (logior #xEC (if (eql class :gpr-8) 0 1)))
(return-from instruction t))
(when (immediatep port)
(maybe-emit-operand-size-override class)
(emit (logior #xE4 (if (eql class :gpr-8) 0 1)))
(emit-imm 1 port)
(return-from instruction t))))
(define-instruction movcr (dst src)
(when (and (eql (reg-class dst) :cr)
(eql (reg-class src) (if (= *cpu-mode* 64) :gpr-64 :gpr-32)))
;; set cr
(when (register-requires-rex-p src)
(emit-rex :b t))
(emit #x0F #x22 (encode-modrm 3 (reg-number src) (reg-number dst)))
(return-from instruction t))
(when (and (eql (reg-class src) :cr)
(eql (reg-class dst) (if (= *cpu-mode* 64) :gpr-64 :gpr-32)))
;; get cr
(when (register-requires-rex-p dst)
(emit-rex :b t))
(emit #x0F #x20 (encode-modrm 3 (reg-number dst) (reg-number src)))
(return-from instruction t)))