changelog shortlog graph tags branches changeset files file revisions raw help

Mercurial > org > blog / annotate draft/a-bit-of-risc.org

changeset 20: 889759cafcc2
parent: d77884ec2b44
child: 1204cefcfd28
author: Richard Westhaver <ellis@rwest.io>
date: Thu, 06 Jun 2024 23:15:38 -0400
permissions: -rw-r--r--
description: style update
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
1
 #+title: A Bit of RISC
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
2
 #+date: [2024-03-11 Mon]
20
889759cafcc2 style update
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
3
 #+setupfile: ../../../clean.theme
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
4
 I recently picked up [[https://dl.acm.org/doi/10.5555/2462741][Hacker's Delight]] and having a lot of fun with
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
5
 it. It's a collection of bit-manipulation tricks collected by hackers
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
6
 over many years. You can flip open pretty much anywhere in the book
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
7
 and start learn something really cool.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
8
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
9
 There's something about seeing bit strings and assembly code in a book
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
10
 that really catches my attention, this one goes a bit further by even
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
11
 describing a complete RISC (Reduced Instruction Set Computer) we can
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
12
 implement to play around with the various tricks.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
13
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
14
 As an exercise and for fun, I'd like to employ some Lisp-fu here and
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
15
 implement a small VM specifically designed for mangling bits.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
16
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
17
 As a fair warning, I'm not a mathematician and I don't write proofs
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
18
 often. If I get something wrong or there is a better way of doing
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
19
 things, let me know!
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
20
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
21
 You can find most of the code from the book [[https://github.com/hcs0/Hackers-Delight][here]].
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
22
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
23
 * Design
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
24
 ** Data Representation
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
25
 We'll be sticking with a 32-bit word length as recommended in the
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
26
 Preface. We will also represent registers as integers whenever
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
27
 possible, instead of say a bit-vector. Without going into too much
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
28
 detail, it's much more efficient to do bitwise ops on integers
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
29
 instead of bit-vectors in Lisp.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
30
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
31
 We need a minimum of 16 general purpose registers, typically of word
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
32
 length, with R0 reserved for a constant 0. To address 16 different
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
33
 registers we actually only need 4 bits - 5-bits if we needed 32, 6 for
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
34
 64, etc.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
35
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
36
 Floating-point support and special purpose registers are not required.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
37
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
38
 ** Instructions
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
39
 The Hacker's Delight RISC architecture is described in two tables,
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
40
 denoted =basic RISC= and =full RISC= respectively.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
41
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
42
 Most instructions take two source registers =RA= and =RB= with a
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
43
 destination register =RT=. The actual general-purpose registers are
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
44
 labelled =R0= (containg the constant 0) through =R15=.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
45
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
46
 A 3-Address machine is assumed and some instructions take 16-bit
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
47
 signed or unsigned immediate values - denoted =I= and =Iu=
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
48
 respectively.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
49
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
50
 #+tblname: Basic Instruction Set (basic RISC)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
51
 | Opcode Mnemonic                                                 | Operands  | Description                                                                                                                             |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
52
 |-----------------------------------------------------------------+-----------+-----------------------------------------------------------------------------------------------------------------------------------------|
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
53
 | add,sub,mul,div,divu,rem,remu                                   | RT,RA,RB  | RT <- (op RA RB)                                                                                                                        |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
54
 | addi,muli                                                       | RT,RA,I   | RT <- (op RA I), I is a 16-bit signed immediate-value                                                                                   |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
55
 | addis                                                           | RT,RA,I   | RT <- (+ RA (\vert\vert I 0x0000))                                                                                                      |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
56
 | and,or,xor                                                      | RT,RA,RB  | RT <- (op RA RB)                                                                                                                        |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
57
 | andi,ori,xori                                                   | RT,RA,Iu  | As above, expect the last operand is a 16-bit unsigned immediate-value                                                                  |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
58
 | beq,bne,blt,ble,bgt,bge                                         | RT,target | Branch to target if (op RT)                                                                                                             |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
59
 | bt,bf                                                           | RT,target | Branch true/false, same as bne/beq resp                                                                                                 |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
60
 | cmpeq,cmpne,cmplt,cmple,cmpgt,cmpge,cmpltu,cmpleu,cmpgtu,cmpgeu | RT,RA,RB  | RT <- (if (op RA RB) 1 0)                                                                                                               |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
61
 | cmpieq,cmpine,cmpilt,cmpile,cmpigt,cmpige                       | RT,RA,I   | Like cmpeq except second comparand is a 16-bit signed immediate-value                                                                   |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
62
 | cmpiequ,cmpineu,cmpiltu,cmpileu,cmpigtu,cmpigeu                 | RT,RA,I   | Like cmpltu except second comparand is a 16-bit unsigned immediate-value                                                                |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
63
 | ldbu,ldh,ldhu,ldw                                               | RT,d(RA)  | Load an unsigned-byte, signed-halfword, unsigned-halfword, or word into RT from (+ RA d) where d is a 16-bit signed immediate-value     |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
64
 | mulhs,mulhu                                                     | RT,RA,RB  | RT gets the high-order 32 bits of (* RA RB)                                                                                             |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
65
 | not                                                             | RT,RA     | RT <- bitwise one's-complement of RA                                                                                                    |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
66
 | shl,shr,shrs                                                    | RT,RA,RB  | RT <- RA shifted left or right by rightmost six bits of RB; 0-fill except for shrs, which is sign-fill (shift amount treated modulo 64) |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
67
 | shli,shri,shrsi                                                 | RT,RA,Iu  | RT <- RA shifted left or right by 5-bit immediate field                                                                                 |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
68
 | stb,sth,stw                                                     | RS,d(RA)  | Store a byte,halfword,word from RS into memory at location (+ RA d) where d is a 16-bit signed immediate-value                          |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
69
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
70
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
71
 #+name: Addition Instructions (full RISC)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
72
 | Opcode Mnemonic                                                 | Operands  | Description                                                                                            |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
73
 |-----------------------------------------------------------------+-----------+--------------------------------------------------------------------------------------------------------|
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
74
 | abs,nabs                                                        | RT,RA     | RT <- (op RA)                                                                                          |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
75
 | andc,eqv,nand,nor,orc                                           | RT,RA,RB  | RT <- (op RA RB)                                                                                       |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
76
 | extr                                                            | RT,RA,I,L | extract bits I through I+L-1 of RA and place them right-adjusted in RT, with 0-fill                    |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
77
 | extrs                                                           | RT,RA,I,L | Like extr, but sign-fill                                                                               |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
78
 | ins                                                             | RT,RA,I,L | Insert bits 0 through L-1 of RA into bits I through I+L-1 of RT                                        |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
79
 | nlz                                                             | RT,RA     | RT gets count of leading 0's in RA (0 to 32)                                                           |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
80
 | pop                                                             | RT,RA     | RT gets the number of 1-bits in RA (0 to 32)                                                           |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
81
 | ldb                                                             | RT,d(RA)  | Load a signed byte into RT from memory at location (+ RA d) where d is a 16-bit signed immediate value |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
82
 | moveq,movne,movlt,movle,movgt,movge                             | RT,RA,RB  | RT <- RA rotate-shifted left or right by the rightmost 5-bits of RB                                    |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
83
 | shlr,shrr                                                       | RT,RA,RB  | RT <- RA rotate-shifted left or right by the rightmost 5-bits of RB                                    |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
84
 | shlri,shrri                                                     | RT,RA,Iu  | RT <- RA rotate-shifted left or right by the 5-bit immediate field                                     |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
85
 | trpeq,trpne,trplt,trple,trpgt,trpge,trpltu,trpleu,trpgtu,trpgeu | RA,RB     | Trap (interrupt) if (op RA RB)                                                                         |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
86
 | trpieq,trpine,trpilt,trpile,trpigt,trpige                       | RA,I      | Trap if (op RA I) where I is a 16-bit signed immediate-value                                           |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
87
 | trpiequ,trpineu,trpiltu,trpileu,trpigtu,trpigeu                 | RA,Iu     | Trap if (op RA Iu) where Iu is a 16-bit unsigned immediate-value                                       |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
88
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
89
 There is also some extensions, which are like macros that usually
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
90
 expand to a single instruction.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
91
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
92
 #+name: Extended Mnemonics
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
93
 | Extended Mnemonic | Expansion        | Description               |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
94
 |-------------------+------------------+---------------------------|
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
95
 | b target          | beq R0,target    | Unconditional branch      |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
96
 | li RT,I           | (addi,addis,ori) | Load immediate            |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
97
 | mov RT,RA         | ori RT,RA,0      | Move register RA to RT    |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
98
 | neg RT,RA         | sub RT,R0,RA     | Negate (two's-complement) |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
99
 | subi RT,RA,I      | addi RT,RA,-I    | Subtract immediate        |
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
100
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
101
 All of these instructions are available on x86,arm,riscv and the likes
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
102
 so no real surprises. We will implement the basic set in Lisp, mapping
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
103
 instructions directly to Lisp functions using macros.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
104
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
105
 ** Execution Model
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
106
 We'll build this machine in Lisp and use plenty intrinsics from
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
107
 SBCL. As a starting point I followed Paul Khuong's excellent blog
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
108
 post: [[https://pvk.ca/Blog/2014/03/15/sbcl-the-ultimate-assembly-code-breadboard/][SBCL: The ultimate assembly code breadboard]].
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
109
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
110
 Some things to keep in mind for our machine:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
111
 - every instruction requires at most two register reads and one
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
112
   register write - good for compilers
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
113
 - every instruction counts as a single cycle
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
114
 - we pay no attention to instruction-level parallelism
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
115
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
116
 * The HAKMEM VM
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
117
 :properties:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
118
 :header-args: :session t :results none
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
119
 :end:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
120
 #+name: defpackage
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
121
 #+begin_src lisp
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
122
   (ql:quickload :prelude)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
123
   (in-package :std-user)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
124
   (defpackage :hakmem
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
125
     (:use :cl :std)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
126
     (:import-from :sb-assem :inst)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
127
     (:import-from :sb-vm :immediate-constant :registers :zero :ea))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
128
   (in-package :hakmem)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
129
   ;; (in-package :sb-x86-64-asm)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
130
   (in-readtable :std)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
131
   (declaim (optimize (speed 3) (safety 1)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
132
   (defconstant +word-size+ 32 "default word size and register length.")
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
133
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
134
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
135
 #+name: vars
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
136
 #+begin_src lisp :package hakmem
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
137
   (declaim (type (unsigned-byte #.+word-size+) +ro+))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
138
   (defconstant +r0+ 0 "constant value for register 0")
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
139
   (defvar *stack* (make-array 8 :initial-contents (list sb-vm::r8-tn
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
140
                                                         sb-vm::r9-tn
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
141
                                                         sb-vm::r10-tn
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
142
                                                         sb-vm::r11-tn
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
143
                                                         sb-vm::r12-tn
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
144
                                                         sb-vm::r13-tn
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
145
                                                         sb-vm::r14-tn
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
146
                                                         sb-vm::r15-tn)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
147
   (defvar *stack-pointer*)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
148
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
149
   (defvar *rax* sb-vm::rax-tn)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
150
   (defvar *rbx* sb-vm::rax-tn)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
151
   (defvar *rcx* sb-vm::rax-tn)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
152
   (defvar *rdx* sb-vm::rax-tn)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
153
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
154
   ;; (@ 0) returns the (current) register for TOS, (@ 1) returns
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
155
   ;; the one just below, etc.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
156
   (defun @ (i)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
157
     (aref *stack* (mod (+ i *stack-pointer*) (length *stack*))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
158
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
159
   (defvar *code-base* sb-vm::rsi-tn)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
160
   (defvar *virtual-ip* sb-vm::rdi-tn)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
161
   (sb-x86-64-asm::get-gpr :qword 4)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
162
   ;; (sb-vm::immediate-constant-sc 10000)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
163
   ;; arena vector or list?
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
164
   (defvar *instructions* (make-hash-table :test #'equal))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
165
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
166
   (defvar *primitive-code-offset* (* 64 67))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
167
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
168
   (defstruct code-page
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
169
     (alloc 0) ;; next free byte
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
170
     (code (make-array *primitive-code-offset* :element-type 'octet)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
171
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
172
   (defun emit-code (pages emitter)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
173
     ;; there must be as many code pages as there are stack slots
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
174
     (assert (= (length *stack*) (length pages)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
175
     ;; find the rightmost starting point, and align to 16 bytes
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
176
     (let* ((alloc (logandc2 (+ 15 (reduce #'max pages :key #'code-page-alloc))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
177
                             15))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
178
            (bytes (loop for i below (length pages)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
179
                         for page = (elt pages i)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
180
                         collect (let ((segment (sb-assem:make-segment))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
181
                                       (*stack-pointer* i))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
182
                                   ;; assemble the variant for this value
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
183
                                   ;; of *stack-pointer* in a fresh code
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
184
                                   ;; segment
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
185
                                   (sb-assem:assemble (segment)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
186
                                     ;; but first, insert padding
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
187
                                     (sb-vm::emit-long-nop segment (- alloc (code-page-alloc page)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
188
                                     (funcall emitter))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
189
                                   ;; tidy up any backreference
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
190
                                   (sb-assem:finalize-segment segment)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
191
                                   ;; then get the (position-independent) machine
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
192
                                   ;; code as a vector of bytes
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
193
                                   (sb-assem:segment-contents-as-vector segment)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
194
       ;; finally, copy each machine code sequence to the right code page
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
195
       (map nil (lambda (page bytes)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
196
                  (let ((alloc (code-page-alloc page)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
197
                    (replace (code-page-code page) bytes :start1 alloc)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
198
                    (assert (<= (+ alloc (length bytes)) (length (code-page-code page))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
199
                    (setf (code-page-alloc page) (+ alloc (length bytes)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
200
            pages bytes)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
201
       ;; and return the offset for that code sequence
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
202
       alloc))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
203
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
204
   (defun emit-all-code (&rest emitters)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
205
     (let ((pages (loop repeat (length *stack*)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
206
                        for page = (make-code-page)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
207
                        ;; prefill everything with one-byte NOPs
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
208
                        do (fill (code-page-code page) #x90)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
209
                        collect page)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
210
       (values (mapcar (lambda (emitter)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
211
                         (emit-code pages emitter))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
212
                       emitters)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
213
               pages)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
214
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
215
   (defun next (&optional offset)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
216
     (setf offset (or offset 0)) ; accommodate primops that frob IP
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
217
     (let ((rotation (mod *stack-pointer* (length *stack*))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
218
       (inst movzx *rax* (make-ea :dword :base *virtual-ip*
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
219
                                         :disp offset))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
220
       (unless (= -4 offset)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
221
         (inst add *virtual-ip* (+ 4 offset)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
222
       (if (zerop rotation)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
223
           (inst add *rax* *code-base*)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
224
           (inst lea *rax* (make-ea :qword :base *code-base*
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
225
                                           :index *rax*
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
226
                                           :disp (* rotation *primitive-code-offset*))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
227
       (inst jmp *rax*)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
228
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
229
   (defun swap ()
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
230
     (inst xchg (@ 0) (@ 1)) ; exchange top of stack and stack[1]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
231
     (next))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
232
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
233
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
234
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
235
 #+name: instructions
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
236
 #+begin_src lisp :package hakmem
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
237
   ;; todo
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
238
   (defun %parse-reg3 (rt ra rb))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
239
   (defun %parse-reg2i (rt ra i))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
240
   (defun %parse-reg2ui (rt ra ui))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
241
   (defmacro def-inst (name args &body body)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
242
       ;; todo: compose a function based on regs+args+body
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
243
       `(let ((sc *scratch*)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
244
              (r0 +r0+)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
245
              (ra 0)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
246
              (rb 0)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
247
              (rt 0))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
248
          (declare (ignorable sc r0 ra rb rt))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
249
          (setf (gethash ',name *instructions*) (lambda ,args (progn ,@body)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
250
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
251
   (defmacro def-prim (name op)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
252
     `(def-inst ,name () (setf rt (,op ra rb))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
253
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
254
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
255
 #+name: prims
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
256
 #+begin_src lisp :package hakmem
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
257
   (def-prim add +)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
258
   (def-prim sub -)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
259
   (def-prim mul *)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
260
   (def-prim div /)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
261
   ;; divu
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
262
   (def-prim rem mod)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
263
   ;; remu
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
264
   (def-prim cmpeq =)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
265
   (def-prim cmpne /=)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
266
   (def-prim cmplt <)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
267
   (def-prim cmple <=)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
268
   (def-prim cmpgt >)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
269
   (def-prim cmpge >=)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
270
   ;; ltu leu gtu geu
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
271
   (def-inst addi (i)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
272
     (setf rt (+ ra i)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
273
   (def-inst muli (i)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
274
     (setf rt (* ra i)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
275
   (def-prim and logand)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
276
   (def-prim or logior)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
277
   (def-prim xor logxor)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
278
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
279
   (defun get-inst (i) (gethash i *instructions*))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
280
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
281
   (defmacro %inst (i &body args)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
282
     `(funcall (get-inst ',i) ,@args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
283
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
284
   (defun list-instructions (&optional (tbl *instructions*))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
285
     (hash-table-alist tbl))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
286
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
287
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
288
 #+name: instruction-list
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
289
 #+begin_src lisp :results replace :exports both :package hakmem
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
290
   (list-instructions)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
291
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
292
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
293
 #+RESULTS: instruction-list
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
294
 #+begin_example
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
295
 ((XOR . #<FUNCTION (LAMBDA ()) {10077C774B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
296
  (OR . #<FUNCTION (LAMBDA ()) {10077C777B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
297
  (AND . #<FUNCTION (LAMBDA ()) {10077C77AB}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
298
  (MULI . #<FUNCTION (LAMBDA (I)) {10077C77DB}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
299
  (ADDI . #<FUNCTION (LAMBDA (I)) {10077C780B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
300
  (CMPGE . #<FUNCTION (LAMBDA ()) {10077C783B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
301
  (CMPGT . #<FUNCTION (LAMBDA ()) {10077C786B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
302
  (CMPLE . #<FUNCTION (LAMBDA ()) {10077C789B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
303
  (CMPLT . #<FUNCTION (LAMBDA ()) {10077C78CB}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
304
  (CMPNE . #<FUNCTION (LAMBDA ()) {10077C78FB}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
305
  (CMPEQ . #<FUNCTION (LAMBDA ()) {10077C792B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
306
  (REM . #<FUNCTION (LAMBDA ()) {10077C795B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
307
  (DIV . #<FUNCTION (LAMBDA ()) {10077C79AB}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
308
  (MUL . #<FUNCTION (LAMBDA ()) {10077C79EB}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
309
  (SUB . #<FUNCTION (LAMBDA ()) {10077C7A1B}>)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
310
  (ADD . #<FUNCTION (LAMBDA ()) {10077C7A4B}>))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
311
 #+end_example