Raymond Toy pushed to branch arm64-dev-1 at cmucl / cmucl

Commits:

1 changed file:

Changes:

  • src/compiler/arm64/macros.lisp
    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
    +