|
|
1
|
+;;; -*- Package: ARM64 -*-
|
|
|
2
|
+;;;
|
|
|
3
|
+;;; **********************************************************************
|
|
|
4
|
+;;; This code was written as part of the CMU Common Lisp project at
|
|
|
5
|
+;;; Carnegie Mellon University, and has been placed in the public domain.
|
|
|
6
|
+;;;
|
|
|
7
|
+(ext:file-comment
|
|
|
8
|
+ "$Header: src/compiler/arm64/macros.lisp $")
|
|
|
9
|
+;;;
|
|
|
10
|
+;;; **********************************************************************
|
|
|
11
|
+;;;
|
|
|
12
|
+;;; This file contains various useful macros for generating ARM64 code.
|
|
|
13
|
+;;;
|
|
|
14
|
+;;; Written by [ARM64 port contributors].
|
|
|
15
|
+;;; Derived from the ARM and SPARC ports.
|
|
|
16
|
+;;;
|
|
|
17
|
+
|
|
|
18
|
+(in-package "ARM64")
|
|
|
19
|
+(intl:textdomain "cmucl-arm64-vm")
|
|
|
20
|
+
|
|
|
21
|
+
|
|
|
22
|
+;;; Instruction-like macros.
|
|
|
23
|
+
|
|
|
24
|
+(defmacro move (dst src)
|
|
|
25
|
+ "Move SRC into DST unless they are location=."
|
|
|
26
|
+ (once-only ((n-dst dst)
|
|
|
27
|
+ (n-src src))
|
|
|
28
|
+ `(unless (location= ,n-dst ,n-src)
|
|
|
29
|
+ (inst mov ,n-dst ,n-src))))
|
|
|
30
|
+
|
|
|
31
|
+;; (loadw object base &optional (offset 0) (lowtag 0) temp)
|
|
|
32
|
+;; (storew object base &optional (offset 0) (lowtag 0) temp)
|
|
|
33
|
+;;
|
|
|
34
|
+;; Load a word at a given address into the register OBJECT, or store
|
|
|
35
|
+;; OBJECT at the given address. The address of the word is in register
|
|
|
36
|
+;; BASE, plus an offset given by OFFSET, which is in words. LOWTAG is
|
|
|
37
|
+;; an adjustment to OFFSET to account for any tag bits used in the BASE
|
|
|
38
|
+;; descriptor register.
|
|
|
39
|
+;;
|
|
|
40
|
+;; On AArch64 we use LDUR/STUR which take an exact unscaled signed-byte 9
|
|
|
41
|
+;; byte offset (-256..255). When the offset does not fit, TEMP is loaded
|
|
|
42
|
+;; with the full offset and the register-offset form of LDR/STR is used.
|
|
|
43
|
+;; If TEMP is not supplied the offset is assumed to fit in a signed-byte 9.
|
|
|
44
|
+(macrolet
|
|
|
45
|
+ ((def-load/store-word (op inst reg-inst shift)
|
|
|
46
|
+ `(defmacro ,op (object base &optional (offset 0) (lowtag 0) temp)
|
|
|
47
|
+ (if temp
|
|
|
48
|
+ (let ((offs (gensym)))
|
|
|
49
|
+ `(let ((,offs (- (ash ,offset ,',shift) ,lowtag)))
|
|
|
50
|
+ (if (typep ,offs '(signed-byte 9))
|
|
|
51
|
+ (inst ,',inst ,object ,base ,offs)
|
|
|
52
|
+ (progn
|
|
|
53
|
+ (inst li ,temp ,offs)
|
|
|
54
|
+ (inst ,',reg-inst ,object (reg-offset ,base ,temp))))))
|
|
|
55
|
+ `(inst ,',inst ,object ,base (- (ash ,offset ,',shift) ,lowtag))))))
|
|
|
56
|
+ (def-load/store-word loadw ldur ldr word-shift)
|
|
|
57
|
+ (def-load/store-word storew stur str word-shift))
|
|
|
58
|
+
|
|
|
59
|
+(defmacro load-symbol (reg symbol)
|
|
|
60
|
+ `(inst add ,reg null-tn (static-symbol-offset ,symbol)))
|
|
|
61
|
+
|
|
|
62
|
+(macrolet
|
|
|
63
|
+ ((frob (slot)
|
|
|
64
|
+ (let ((loader (intern (concatenate 'simple-string
|
|
|
65
|
+ "LOAD-SYMBOL-"
|
|
|
66
|
+ (string slot))))
|
|
|
67
|
+ (storer (intern (concatenate 'simple-string
|
|
|
68
|
+ "STORE-SYMBOL-"
|
|
|
69
|
+ (string slot))))
|
|
|
70
|
+ (offset (intern (concatenate 'simple-string
|
|
|
71
|
+ "SYMBOL-"
|
|
|
72
|
+ (string slot)
|
|
|
73
|
+ "-SLOT")
|
|
|
74
|
+ (find-package "VM"))))
|
|
|
75
|
+ `(progn
|
|
|
76
|
+ (defmacro ,loader (reg symbol)
|
|
|
77
|
+ `(inst ldur ,reg null-tn
|
|
|
78
|
+ (+ (static-symbol-offset ',symbol)
|
|
|
79
|
+ (ash ,',offset word-shift)
|
|
|
80
|
+ (- other-pointer-type))))
|
|
|
81
|
+ (defmacro ,storer (reg symbol)
|
|
|
82
|
+ `(inst stur ,reg null-tn
|
|
|
83
|
+ (+ (static-symbol-offset ',symbol)
|
|
|
84
|
+ (ash ,',offset word-shift)
|
|
|
85
|
+ (- other-pointer-type))))))))
|
|
|
86
|
+ (frob value)
|
|
|
87
|
+ (frob function))
|
|
|
88
|
+
|
|
|
89
|
+(defmacro load-type (target source &optional (offset 0))
|
|
|
90
|
+ "Loads the type bits of a pointer into target independent of
|
|
|
91
|
+ byte-ordering issues."
|
|
|
92
|
+ (once-only ((n-target target)
|
|
|
93
|
+ (n-source source)
|
|
|
94
|
+ (n-offset offset))
|
|
|
95
|
+ (ecase (backend-byte-order *target-backend*)
|
|
|
96
|
+ (:little-endian
|
|
|
97
|
+ `(inst ldurb ,n-target ,n-source ,n-offset))
|
|
|
98
|
+ (:big-endian
|
|
|
99
|
+ `(inst ldurb ,n-target ,n-source (+ ,n-offset (1- word-bytes)))))))
|
|
|
100
|
+
|
|
|
101
|
+;;; Macros to handle the fact that we cannot use the machine native call and
|
|
|
102
|
+;;; return instructions.
|
|
|
103
|
+;;;
|
|
|
104
|
+;;; On AArch64 there is no indirect-branch-through-register instruction that
|
|
|
105
|
+;;; also sets the link register; instead we use BR (branch to register) for
|
|
|
106
|
+;;; computed jumps and compute the target address explicitly via LIP-TN.
|
|
|
107
|
+;;; The hardware link register (X30 / LR-TN) is reserved for the C ABI;
|
|
|
108
|
+;;; Lisp uses its own LRA convention.
|
|
|
109
|
+
|
|
|
110
|
+(defmacro lisp-jump (function)
|
|
|
111
|
+ "Jump to the lisp function FUNCTION. LIP-TN is an interior-reg temporary."
|
|
|
112
|
+ `(progn
|
|
|
113
|
+ (inst add lip-tn ,function
|
|
|
114
|
+ (- (ash function-code-offset word-shift) vm:function-pointer-type))
|
|
|
115
|
+ (move code-tn ,function)
|
|
|
116
|
+ (inst br lip-tn)))
|
|
|
117
|
+
|
|
|
118
|
+(defmacro lisp-return (return-pc &key (offset 0) (frob-code t))
|
|
|
119
|
+ "Return to RETURN-PC."
|
|
|
120
|
+ `(progn
|
|
|
121
|
+ (inst add lip-tn ,return-pc
|
|
|
122
|
+ (- (* (1+ ,offset) word-bytes) other-pointer-type))
|
|
|
123
|
+ ,(when frob-code
|
|
|
124
|
+ `(move code-tn ,return-pc))
|
|
|
125
|
+ (inst br lip-tn)))
|
|
|
126
|
+
|
|
|
127
|
+(defmacro emit-return-pc (label)
|
|
|
128
|
+ "Emit a return-pc header word. LABEL is the label to use for this return-pc."
|
|
|
129
|
+ `(progn
|
|
|
130
|
+ (align lowtag-bits)
|
|
|
131
|
+ (emit-label ,label)
|
|
|
132
|
+ (inst lra-header-word)))
|
|
|
133
|
+
|
|
|
134
|
+
|
|
|
135
|
+;;;; Stack TNs
|
|
|
136
|
+
|
|
|
137
|
+;;; Load-Stack-TN, Store-Stack-TN -- Interface
|
|
|
138
|
+;;;
|
|
|
139
|
+;;; Move a stack TN to a register and vice-versa.
|
|
|
140
|
+;;;
|
|
|
141
|
+;;; On AArch64 the control stack grows downward. The offset into the
|
|
|
142
|
+;;; frame is therefore negated relative to CFP-TN, matching the ARM port.
|
|
|
143
|
+;;; Large offsets that do not fit in the LDR/STR immediate field require a
|
|
|
144
|
+;;; temporary register; callers that care about very deep frames should use
|
|
|
145
|
+;;; LOADW/STOREW with an explicit TEMP argument instead.
|
|
|
146
|
+(defmacro load-stack-tn (reg stack)
|
|
|
147
|
+ `(let ((reg ,reg)
|
|
|
148
|
+ (stack ,stack))
|
|
|
149
|
+ (sc-case stack
|
|
|
150
|
+ ((control-stack)
|
|
|
151
|
+ ;; Stack grows down, so negate the TN offset.
|
|
|
152
|
+ (loadw reg cfp-tn (- (tn-offset stack)) 0)))))
|
|
|
153
|
+
|
|
|
154
|
+(defmacro store-stack-tn (stack reg)
|
|
|
155
|
+ `(let ((stack ,stack)
|
|
|
156
|
+ (reg ,reg))
|
|
|
157
|
+ (sc-case stack
|
|
|
158
|
+ ((control-stack)
|
|
|
159
|
+ ;; Stack grows down, so negate the TN offset.
|
|
|
160
|
+ (storew reg cfp-tn (- (tn-offset stack)) 0)))))
|
|
|
161
|
+
|
|
|
162
|
+
|
|
|
163
|
+;;; MAYBE-LOAD-STACK-TN -- Interface
|
|
|
164
|
+;;;
|
|
|
165
|
+(defmacro maybe-load-stack-tn (reg reg-or-stack)
|
|
|
166
|
+ "Move the TN Reg-Or-Stack into Reg if it isn't already there."
|
|
|
167
|
+ (once-only ((n-reg reg)
|
|
|
168
|
+ (n-stack reg-or-stack))
|
|
|
169
|
+ `(sc-case ,n-reg
|
|
|
170
|
+ ((any-reg descriptor-reg)
|
|
|
171
|
+ (sc-case ,n-stack
|
|
|
172
|
+ ((any-reg descriptor-reg)
|
|
|
173
|
+ (move ,n-reg ,n-stack))
|
|
|
174
|
+ ((control-stack)
|
|
|
175
|
+ (loadw ,n-reg cfp-tn (tn-offset ,n-stack))))))))
|
|
|
176
|
+
|
|
|
177
|
+
|
|
|
178
|
+;;;; Storage allocation:
|
|
|
179
|
+
|
|
|
180
|
+;; Allocation macro
|
|
|
181
|
+;;
|
|
|
182
|
+;; This macro does the appropriate stuff to allocate space.
|
|
|
183
|
+;;
|
|
|
184
|
+;; The allocated space is stored in RESULT-TN with the lowtag LOWTAG
|
|
|
185
|
+;; applied. The amount of space to be allocated is SIZE bytes (which
|
|
|
186
|
+;; must be a multiple of the lisp object size).
|
|
|
187
|
+;;
|
|
|
188
|
+;; If STACK-P is given, then allocation occurs on the control stack
|
|
|
189
|
+;; (for dynamic-extent). In this case, you MUST also specify NODE, so
|
|
|
190
|
+;; that the appropriate compiler policy can be used, and TEMP-TN,
|
|
|
191
|
+;; which is needed for work-space. TEMP-TN MUST be a non-descriptor
|
|
|
192
|
+;; reg.
|
|
|
193
|
+;;
|
|
|
194
|
+;; TEMP-TN MUST always be supplied because a temp register is needed
|
|
|
195
|
+;; to do inline allocation.
|
|
|
196
|
+;;
|
|
|
197
|
+;; The ALLOC-TN register (X14) holds the current heap frontier and
|
|
|
198
|
+;; carries the pseudo-atomic flag in bit 0, exactly as on SPARC.
|
|
|
199
|
+;; We assume we're in a pseudo-atomic so the pseudo-atomic bit is set.
|
|
|
200
|
+(defmacro allocation (result-tn size lowtag &key stack-p temp-tn)
|
|
|
201
|
+ `(cond (,stack-p
|
|
|
202
|
+ ;; Stack allocation
|
|
|
203
|
+ ;;
|
|
|
204
|
+ ;; The control stack grows down on AArch64. Round CSP down to
|
|
|
205
|
+ ;; a multiple of the lispobj size, use that as the allocation
|
|
|
206
|
+ ;; pointer, then subtract SIZE to claim the space.
|
|
|
207
|
+
|
|
|
208
|
+ ;; Make sure the temp-tn is a non-descriptor register!
|
|
|
209
|
+ (assert (and ,temp-tn (sc-is ,temp-tn non-descriptor-reg)))
|
|
|
210
|
+
|
|
|
211
|
+ ;; temp-tn is csp-tn rounded down to a multiple of the lispobj size.
|
|
|
212
|
+ (inst and ,temp-tn csp-tn (lognot vm:lowtag-mask))
|
|
|
213
|
+ ;; Set the result to temp-tn, with appropriate lowtag.
|
|
|
214
|
+ (inst orr ,result-tn ,temp-tn ,lowtag)
|
|
|
215
|
+
|
|
|
216
|
+ ;; Allocate the desired space on the stack.
|
|
|
217
|
+ (inst sub csp-tn ,temp-tn ,size))
|
|
|
218
|
+ (t
|
|
|
219
|
+ (let ((not-overflow (gen-label)))
|
|
|
220
|
+ ;; See if we can do an inline allocation. The updated free
|
|
|
221
|
+ ;; pointer should not point past the end of the current region.
|
|
|
222
|
+ ;; If it does, a full alloc needs to be done.
|
|
|
223
|
+ (load-symbol-value ,result-tn *current-region-end-addr*)
|
|
|
224
|
+
|
|
|
225
|
+ ;; Sometimes the size is a known constant but won't fit in the
|
|
|
226
|
+ ;; 12-bit immediate field of an ADD instruction. Materialise it
|
|
|
227
|
+ ;; in TEMP-TN in that case.
|
|
|
228
|
+ (cond ((and (tn-p ,temp-tn)
|
|
|
229
|
+ (numberp ,size)
|
|
|
230
|
+ (not (typep ,size '(unsigned-byte 12))))
|
|
|
231
|
+ (inst li ,temp-tn ,size)
|
|
|
232
|
+ (inst add alloc-tn alloc-tn ,temp-tn))
|
|
|
233
|
+ (t
|
|
|
234
|
+ (inst add alloc-tn alloc-tn ,size)))
|
|
|
235
|
+
|
|
|
236
|
+ (inst and ,temp-tn alloc-tn (lognot lowtag-mask)) ; Zap PA bits
|
|
|
237
|
+
|
|
|
238
|
+ ;; temp-tn points to the new end of region. Did we go past the
|
|
|
239
|
+ ;; actual end of the region? If so, we need a full alloc.
|
|
|
240
|
+ (inst cmp ,temp-tn ,result-tn)
|
|
|
241
|
+ (without-scheduling ()
|
|
|
242
|
+ ;; NOTE: alloc-tn has been updated to point to the new end.
|
|
|
243
|
+ ;; But the allocation routines expect alloc-tn to point to the
|
|
|
244
|
+ ;; original free region. Thus, the allocation trap handler
|
|
|
245
|
+ ;; MUST subtract SIZE from alloc-tn before calling the alloc
|
|
|
246
|
+ ;; routine. This allows for (slightly) faster inline code.
|
|
|
247
|
+
|
|
|
248
|
+ ;; As above, SIZE might not fit in the immediate field.
|
|
|
249
|
+ (cond ((and (tn-p ,temp-tn)
|
|
|
250
|
+ (numberp ,size)
|
|
|
251
|
+ (not (typep ,size '(unsigned-byte 12))))
|
|
|
252
|
+ (inst li ,result-tn ,size)
|
|
|
253
|
+ (inst sub ,result-tn ,temp-tn ,result-tn))
|
|
|
254
|
+ (t
|
|
|
255
|
+ (inst sub ,result-tn ,temp-tn ,size)))
|
|
|
256
|
+ ;; Branch past the UDF if we did not overflow the region.
|
|
|
257
|
+ (inst b.le not-overflow)
|
|
|
258
|
+ (inst udf allocation-trap))
|
|
|
259
|
+ (emit-label not-overflow)
|
|
|
260
|
+ ;; Set lowtag appropriately.
|
|
|
261
|
+ (inst orr ,result-tn ,result-tn ,lowtag)))))
|
|
|
262
|
+
|
|
|
263
|
+(defmacro with-fixed-allocation ((result-tn temp-tn type-code size
|
|
|
264
|
+ &key (lowtag other-pointer-type)
|
|
|
265
|
+ stack-p)
|
|
|
266
|
+ &body body)
|
|
|
267
|
+ "Do stuff to allocate an other-pointer object of fixed Size with a single
|
|
|
268
|
+ word header having the specified Type-Code. The result is placed in
|
|
|
269
|
+ Result-TN, and Temp-TN is a non-descriptor temp (which may be randomly used
|
|
|
270
|
+ by the body.) The body is placed inside the PSEUDO-ATOMIC, and presumably
|
|
|
271
|
+ initializes the object."
|
|
|
272
|
+ (once-only ((result-tn result-tn) (temp-tn temp-tn)
|
|
|
273
|
+ (type-code type-code) (size size)
|
|
|
274
|
+ (lowtag lowtag))
|
|
|
275
|
+ `(pseudo-atomic ()
|
|
|
276
|
+ (allocation ,result-tn (pad-data-block ,size) ,lowtag
|
|
|
277
|
+ :temp-tn ,temp-tn
|
|
|
278
|
+ :stack-p ,stack-p)
|
|
|
279
|
+ (when ,type-code
|
|
|
280
|
+ (inst li ,temp-tn (logior (ash (1- ,size) type-bits) ,type-code))
|
|
|
281
|
+ (storew ,temp-tn ,result-tn 0 ,lowtag))
|
|
|
282
|
+ ,@body)))
|
|
|
283
|
+
|
|
|
284
|
+
|
|
|
285
|
+;;; PSEUDO-ATOMIC -- Handy macro for making sequences look atomic.
|
|
|
286
|
+;;;
|
|
|
287
|
+;;; On AArch64, as on SPARC, the pseudo-atomic flag lives in bit 0 of
|
|
|
288
|
+;;; ALLOC-TN (X14). Set by ORR-ing in pseudo-atomic-value, cleared by
|
|
|
289
|
+;;; AND-ing with its complement. After clearing, ANDS tests the
|
|
|
290
|
+;;; interrupted flag discarding the result into ZERO-TN (mirroring
|
|
|
291
|
+;;; SPARC's ANDCC ZERO-TN, ALLOC-TN, ...). Because UDF is
|
|
|
292
|
+;;; unconditional (unlike SPARC's "T :NE"), we guard it with a
|
|
|
293
|
+;;; B.EQ skip label. The EXTRA keyword is accepted for compatibility
|
|
|
294
|
+;;; but ignored.
|
|
|
295
|
+(defmacro pseudo-atomic ((&key (extra 0)) &rest forms)
|
|
|
296
|
+ (declare (ignore extra))
|
|
|
297
|
+ (let ((label (gensym "PA-NOT-INTERRUPTED-")))
|
|
|
298
|
+ `(progn
|
|
|
299
|
+ (without-scheduling ()
|
|
|
300
|
+ (inst orr alloc-tn alloc-tn pseudo-atomic-value))
|
|
|
301
|
+ ,@forms
|
|
|
302
|
+ (let ((,label (gen-label)))
|
|
|
303
|
+ (without-scheduling ()
|
|
|
304
|
+ (inst and alloc-tn alloc-tn (lognot pseudo-atomic-value))
|
|
|
305
|
+ (inst ands zero-tn alloc-tn pseudo-atomic-interrupted-value)
|
|
|
306
|
+ (inst b.eq ,label)
|
|
|
307
|
+ (inst udf pseudo-atomic-trap))
|
|
|
308
|
+ (emit-label ,label)))))
|
|
|
309
|
+
|
|
|
310
|
+
|
|
|
311
|
+;;;; Type testing noise.
|
|
|
312
|
+
|
|
|
313
|
+;;; GEN-RANGE-TEST -- internal
|
|
|
314
|
+;;;
|
|
|
315
|
+;;; Generate code that branches to TARGET iff REG contains one of VALUES.
|
|
|
316
|
+;;; If NOT-P is true, invert the test. Jumping to NOT-TARGET is the same
|
|
|
317
|
+;;; as falling out the bottom.
|
|
|
318
|
+;;;
|
|
|
319
|
+;;; On AArch64, CMP sets the condition flags; conditional branches use the
|
|
|
320
|
+;;; standard Bcc mnemonic with a keyword condition code (e.g. :eq, :le).
|
|
|
321
|
+;;; The argument ordering for (inst b target cond) follows the ARM port
|
|
|
322
|
+;;; convention: condition comes second.
|
|
|
323
|
+(defun gen-range-test (reg target not-target not-p min seperation max values)
|
|
|
324
|
+ (let ((tests nil)
|
|
|
325
|
+ (start nil)
|
|
|
326
|
+ (end nil)
|
|
|
327
|
+ (insts nil))
|
|
|
328
|
+ (multiple-value-bind (equal less-or-equal greater-or-equal label)
|
|
|
329
|
+ (if not-p
|
|
|
330
|
+ (values :ne :gt :lt not-target)
|
|
|
331
|
+ (values :eq :le :ge target))
|
|
|
332
|
+ (flet ((emit-test ()
|
|
|
333
|
+ (if (= start end)
|
|
|
334
|
+ (push start tests)
|
|
|
335
|
+ (push (cons start end) tests))))
|
|
|
336
|
+ (dolist (value values)
|
|
|
337
|
+ (cond ((< value min)
|
|
|
338
|
+ (error (intl:gettext "~S is less than the specified minimum of ~S")
|
|
|
339
|
+ value min))
|
|
|
340
|
+ ((> value max)
|
|
|
341
|
+ (error (intl:gettext "~S is greater than the specified maximum of ~S")
|
|
|
342
|
+ value max))
|
|
|
343
|
+ ((not (zerop (rem (- value min) seperation)))
|
|
|
344
|
+ (error (intl:gettext "~S isn't an even multiple of ~S from ~S")
|
|
|
345
|
+ value seperation min))
|
|
|
346
|
+ ((null start)
|
|
|
347
|
+ (setf start value))
|
|
|
348
|
+ ((> value (+ end seperation))
|
|
|
349
|
+ (emit-test)
|
|
|
350
|
+ (setf start value)))
|
|
|
351
|
+ (setf end value))
|
|
|
352
|
+ (emit-test))
|
|
|
353
|
+ (macrolet ((inst (name &rest args)
|
|
|
354
|
+ `(push (list 'inst ',name ,@args) insts)))
|
|
|
355
|
+ (do ((remaining (nreverse tests) (cdr remaining)))
|
|
|
356
|
+ ((null remaining))
|
|
|
357
|
+ (let ((test (car remaining))
|
|
|
358
|
+ (last (null (cdr remaining))))
|
|
|
359
|
+ (if (atom test)
|
|
|
360
|
+ (progn
|
|
|
361
|
+ (inst cmp reg test)
|
|
|
362
|
+ (if last
|
|
|
363
|
+ (inst b target equal)
|
|
|
364
|
+ (inst b label :eq)))
|
|
|
365
|
+ (let ((start (car test))
|
|
|
366
|
+ (end (cdr test)))
|
|
|
367
|
+ (cond ((and (= start min) (= end max))
|
|
|
368
|
+ (warn (intl:gettext "The values ~S cover the entire range from ~
|
|
|
369
|
+ ~S to ~S [step ~S].")
|
|
|
370
|
+ values min max seperation)
|
|
|
371
|
+ (push `(unless ,not-p (inst b ,target)) insts))
|
|
|
372
|
+ ((= start min)
|
|
|
373
|
+ (inst cmp reg end)
|
|
|
374
|
+ (if last
|
|
|
375
|
+ (inst b target less-or-equal)
|
|
|
376
|
+ (inst b label :le)))
|
|
|
377
|
+ ((= end max)
|
|
|
378
|
+ (inst cmp reg start)
|
|
|
379
|
+ (if last
|
|
|
380
|
+ (inst b target greater-or-equal)
|
|
|
381
|
+ (inst b label :ge)))
|
|
|
382
|
+ (t
|
|
|
383
|
+ (inst cmp reg start)
|
|
|
384
|
+ (inst b (if not-p target not-target) :lt)
|
|
|
385
|
+ (inst cmp reg end)
|
|
|
386
|
+ (if last
|
|
|
387
|
+ (inst b target less-or-equal)
|
|
|
388
|
+ (inst b label :le))))))))))
|
|
|
389
|
+ (nreverse insts)))
|
|
|
390
|
+
|
|
|
391
|
+(defun gen-other-immediate-test (reg target not-target not-p values)
|
|
|
392
|
+ (gen-range-test reg target not-target not-p
|
|
|
393
|
+ (+ other-immediate-0-type lowtag-limit)
|
|
|
394
|
+ (- other-immediate-1-type other-immediate-0-type)
|
|
|
395
|
+ (ash 1 type-bits)
|
|
|
396
|
+ values))
|
|
|
397
|
+
|
|
|
398
|
+
|
|
|
399
|
+(defun test-type-aux (reg temp target not-target not-p lowtags immed hdrs
|
|
|
400
|
+ function-p)
|
|
|
401
|
+ (let* ((fixnump (and (member even-fixnum-type lowtags :test #'eql)
|
|
|
402
|
+ (member odd-fixnum-type lowtags :test #'eql)))
|
|
|
403
|
+ (lowtags (sort (if fixnump
|
|
|
404
|
+ (delete even-fixnum-type
|
|
|
405
|
+ (remove odd-fixnum-type lowtags
|
|
|
406
|
+ :test #'eql)
|
|
|
407
|
+ :test #'eql)
|
|
|
408
|
+ (copy-list lowtags))
|
|
|
409
|
+ #'<))
|
|
|
410
|
+ (lowtag (if function-p
|
|
|
411
|
+ vm:function-pointer-type
|
|
|
412
|
+ vm:other-pointer-type))
|
|
|
413
|
+ (hdrs (sort (copy-list hdrs) #'<))
|
|
|
414
|
+ (immed (sort (copy-list immed) #'<)))
|
|
|
415
|
+ (append
|
|
|
416
|
+ (when immed
|
|
|
417
|
+ `((inst and ,temp ,reg type-mask)
|
|
|
418
|
+ ,@(if (or fixnump lowtags hdrs)
|
|
|
419
|
+ (let ((fall-through (gensym)))
|
|
|
420
|
+ `((let (,fall-through (gen-label))
|
|
|
421
|
+ ,@(gen-other-immediate-test
|
|
|
422
|
+ temp (if not-p not-target target)
|
|
|
423
|
+ fall-through nil immed)
|
|
|
424
|
+ (emit-label ,fall-through))))
|
|
|
425
|
+ (gen-other-immediate-test temp target not-target not-p immed))))
|
|
|
426
|
+ (when fixnump
|
|
|
427
|
+ ;; On AArch64, TST is ANDS with Rd = XZR; it sets condition flags
|
|
|
428
|
+ ;; without storing the result.
|
|
|
429
|
+ `((inst tst ,reg fixnum-tag-mask)
|
|
|
430
|
+ ,(if (or lowtags hdrs)
|
|
|
431
|
+ `(inst b ,(if not-p not-target target) :eq)
|
|
|
432
|
+ `(inst b ,target ,(if not-p :ne :eq)))))
|
|
|
433
|
+ (when (or lowtags hdrs)
|
|
|
434
|
+ `((inst and ,temp ,reg lowtag-mask)))
|
|
|
435
|
+ (when lowtags
|
|
|
436
|
+ (if hdrs
|
|
|
437
|
+ (let ((fall-through (gensym)))
|
|
|
438
|
+ `((let ((,fall-through (gen-label)))
|
|
|
439
|
+ ,@(gen-range-test temp (if not-p not-target target)
|
|
|
440
|
+ fall-through nil
|
|
|
441
|
+ 0 1 (1- lowtag-limit) lowtags)
|
|
|
442
|
+ (emit-label ,fall-through))))
|
|
|
443
|
+ (gen-range-test temp target not-target not-p 0 1
|
|
|
444
|
+ (1- lowtag-limit) lowtags)))
|
|
|
445
|
+ (when hdrs
|
|
|
446
|
+ `((inst cmp ,temp ,lowtag)
|
|
|
447
|
+ (inst b ,(if not-p target not-target) :ne)
|
|
|
448
|
+ (load-type ,temp ,reg (- ,lowtag))
|
|
|
449
|
+ ,@(gen-other-immediate-test temp target not-target not-p hdrs))))))
|
|
|
450
|
+
|
|
|
451
|
+(defconstant immediate-types
|
|
|
452
|
+ (list base-char-type unbound-marker-type))
|
|
|
453
|
+
|
|
|
454
|
+(defconstant function-subtypes
|
|
|
455
|
+ (list funcallable-instance-header-type
|
|
|
456
|
+ #-double-double dylan-function-header-type
|
|
|
457
|
+ function-header-type closure-function-header-type
|
|
|
458
|
+ closure-header-type))
|
|
|
459
|
+
|
|
|
460
|
+(defmacro test-type (register temp target not-p &rest type-codes)
|
|
|
461
|
+ (let* ((type-codes (mapcar #'eval type-codes))
|
|
|
462
|
+ (lowtags (remove lowtag-limit type-codes :test #'<))
|
|
|
463
|
+ (extended (remove lowtag-limit type-codes :test #'>))
|
|
|
464
|
+ (immediates (intersection extended immediate-types :test #'eql))
|
|
|
465
|
+ (headers (set-difference extended immediate-types :test #'eql))
|
|
|
466
|
+ (function-p nil))
|
|
|
467
|
+ (unless type-codes
|
|
|
468
|
+ (error (intl:gettext "Must supply at least on type for test-type.")))
|
|
|
469
|
+ (when (and headers (member other-pointer-type lowtags))
|
|
|
470
|
+ (warn (intl:gettext "OTHER-POINTER-TYPE supersedes the use of ~S") headers)
|
|
|
471
|
+ (setf headers nil))
|
|
|
472
|
+ (when (and immediates
|
|
|
473
|
+ (or (member other-immediate-0-type lowtags)
|
|
|
474
|
+ (member other-immediate-1-type lowtags)))
|
|
|
475
|
+ (warn (intl:gettext "OTHER-IMMEDIATE-n-TYPE supersedes the use of ~S") immediates)
|
|
|
476
|
+ (setf immediates nil))
|
|
|
477
|
+ (when (intersection headers function-subtypes)
|
|
|
478
|
+ (unless (subsetp headers function-subtypes)
|
|
|
479
|
+ (error (intl:gettext "Can't test for mix of function subtypes and normal ~
|
|
|
480
|
+ header types.")))
|
|
|
481
|
+ (setq function-p t))
|
|
|
482
|
+
|
|
|
483
|
+ (let ((n-reg (gensym))
|
|
|
484
|
+ (n-temp (gensym))
|
|
|
485
|
+ (n-target (gensym))
|
|
|
486
|
+ (not-target (gensym)))
|
|
|
487
|
+ `(let ((,n-reg ,register)
|
|
|
488
|
+ (,n-temp ,temp)
|
|
|
489
|
+ (,n-target ,target)
|
|
|
490
|
+ (,not-target (gen-label)))
|
|
|
491
|
+ (declare (ignorable ,n-temp))
|
|
|
492
|
+ ,@(if (constantp not-p)
|
|
|
493
|
+ (test-type-aux n-reg n-temp n-target not-target
|
|
|
494
|
+ (eval not-p) lowtags immediates headers
|
|
|
495
|
+ function-p)
|
|
|
496
|
+ `((cond (,not-p
|
|
|
497
|
+ ,@(test-type-aux n-reg n-temp n-target not-target t
|
|
|
498
|
+ lowtags immediates headers
|
|
|
499
|
+ function-p))
|
|
|
500
|
+ (t
|
|
|
501
|
+ ,@(test-type-aux n-reg n-temp n-target not-target nil
|
|
|
502
|
+ lowtags immediates headers
|
|
|
503
|
+ function-p)))))
|
|
|
504
|
+ (emit-label ,not-target)))))
|
|
|
505
|
+
|
|
|
506
|
+
|
|
|
507
|
+;;;; Error Code
|
|
|
508
|
+
|
|
|
509
|
+(defvar *adjustable-vectors* nil)
|
|
|
510
|
+
|
|
|
511
|
+(defmacro with-adjustable-vector ((var) &rest body)
|
|
|
512
|
+ `(let ((,var (or (pop *adjustable-vectors*)
|
|
|
513
|
+ (make-array 16
|
|
|
514
|
+ :element-type '(unsigned-byte 8)
|
|
|
515
|
+ :fill-pointer 0
|
|
|
516
|
+ :adjustable t))))
|
|
|
517
|
+ (setf (fill-pointer ,var) 0)
|
|
|
518
|
+ (unwind-protect
|
|
|
519
|
+ (progn
|
|
|
520
|
+ ,@body)
|
|
|
521
|
+ (push ,var *adjustable-vectors*))))
|
|
|
522
|
+
|
|
|
523
|
+(eval-when (compile load eval)
|
|
|
524
|
+ (defun emit-error-break (vop kind code values)
|
|
|
525
|
+ (let ((vector (gensym)))
|
|
|
526
|
+ `((let ((vop ,vop))
|
|
|
527
|
+ (when vop
|
|
|
528
|
+ (note-this-location vop :internal-error)))
|
|
|
529
|
+ ;; AArch64 uses UDF (permanently undefined instruction) as the
|
|
|
530
|
+ ;; error trap. The KIND immediate is encoded directly in the
|
|
|
531
|
+ ;; UDF instruction word and read by the signal handler.
|
|
|
532
|
+ (inst udf ,kind)
|
|
|
533
|
+ (with-adjustable-vector (,vector)
|
|
|
534
|
+ (write-var-integer (error-number-or-lose ',code) ,vector)
|
|
|
535
|
+ ,@(mapcar #'(lambda (tn)
|
|
|
536
|
+ `(let ((tn ,tn))
|
|
|
537
|
+ (write-var-integer (make-sc-offset (sc-number
|
|
|
538
|
+ (tn-sc tn))
|
|
|
539
|
+ (tn-offset tn))
|
|
|
540
|
+ ,vector)))
|
|
|
541
|
+ values)
|
|
|
542
|
+ (inst byte (length ,vector))
|
|
|
543
|
+ (dotimes (i (length ,vector))
|
|
|
544
|
+ (inst byte (aref ,vector i))))
|
|
|
545
|
+ (align word-shift)))))
|
|
|
546
|
+
|
|
|
547
|
+(defmacro error-call (vop error-code &rest values)
|
|
|
548
|
+ "Cause an error. ERROR-CODE is the error to cause."
|
|
|
549
|
+ (cons 'progn
|
|
|
550
|
+ (emit-error-break vop error-trap error-code values)))
|
|
|
551
|
+
|
|
|
552
|
+(defmacro cerror-call (vop label error-code &rest values)
|
|
|
553
|
+ "Cause a continuable error. If the error is continued, execution resumes at
|
|
|
554
|
+ LABEL."
|
|
|
555
|
+ `(progn
|
|
|
556
|
+ ,@(emit-error-break vop cerror-trap error-code values)
|
|
|
557
|
+ (inst b ,label)))
|
|
|
558
|
+
|
|
|
559
|
+(defmacro generate-error-code (vop error-code &rest values)
|
|
|
560
|
+ "Generate-Error-Code Error-code Value*
|
|
|
561
|
+ Emit code for an error with the specified Error-Code and context Values."
|
|
|
562
|
+ `(assemble (*elsewhere*)
|
|
|
563
|
+ (let ((start-lab (gen-label)))
|
|
|
564
|
+ (emit-label start-lab)
|
|
|
565
|
+ (error-call ,vop ,error-code ,@values)
|
|
|
566
|
+ start-lab)))
|
|
|
567
|
+
|
|
|
568
|
+(defmacro generate-cerror-code (vop error-code &rest values)
|
|
|
569
|
+ "Generate-CError-Code Error-code Value*
|
|
|
570
|
+ Emit code for a continuable error with the specified Error-Code and
|
|
|
571
|
+ context Values. If the error is continued, execution resumes after
|
|
|
572
|
+ the GENERATE-CERROR-CODE form."
|
|
|
573
|
+ (let ((continue (gensym "CONTINUE-LABEL-"))
|
|
|
574
|
+ (error (gensym "ERROR-LABEL-")))
|
|
|
575
|
+ `(let ((,continue (gen-label)))
|
|
|
576
|
+ (emit-label ,continue)
|
|
|
577
|
+ (assemble (*elsewhere*)
|
|
|
578
|
+ (let ((,error (gen-label)))
|
|
|
579
|
+ (emit-label ,error)
|
|
|
580
|
+ (cerror-call ,vop ,continue ,error-code ,@values)
|
|
|
581
|
+ ,error)))))
|
|
|
582
|
+ |