Start on ARM64

Copy stuff from arm32 changing 'arm' to 'arm64'.
This commit is contained in:
Stas Boukarev 2015-06-02 20:31:14 +00:00
parent 1b1e95d6a1
commit d576cb5608
60 changed files with 12427 additions and 18 deletions

View file

@ -252,6 +252,7 @@
#!+alpha ("src/code/alpha-vm" :not-host)
#!+mips ("src/code/mips-vm" :not-host)
#!+arm ("src/code/arm-vm" :not-host)
#!+arm64 ("src/code/arm64-vm" :not-host)
;; FIXME: do we really want to keep this? -- CSR, 2002-08-31
#!+rt ("src/code/rt-vm" :not-host)

View file

@ -733,6 +733,17 @@ elif [ "$sbcl_arch" = "arm" ]; then
printf ' :stack-allocatable-vectors :stack-allocatable-closures' >> $ltf
printf ' :precise-arg-count-error :unwind-to-frame-and-call-vop' >> $ltf
printf ' :fp-and-pc-standard-save' >> $ltf
elif [ "$sbcl_arch" = "arm64" ]; then
printf ' :gencgc' >> $ltf
# printf ' :gencgc :linkage-table :alien-callbacks' >> $ltf
# # As opposed to soft-float or FPA, we support VFP only (and
# # possibly VFPv2 and higher only), but we'll leave the obvious
# # hooks in for someone to add the support later.
printf ' :arm-vfp :arm-vfpv2' >> $ltf
# printf ' :ash-right-vops :multiply-high-vops :symbol-info-vops' >> $ltf
# printf ' :stack-allocatable-lists :stack-allocatable-fixed-objects' >> $ltf
# printf ' :stack-allocatable-vectors :stack-allocatable-closures' >> $ltf
# printf ' :precise-arg-count-error :unwind-to-frame-and-call-vop' >> $ltf
else
# Nothing need be done in this case, but sh syntax wants a placeholder.
echo > /dev/null

View file

@ -0,0 +1,3 @@
(in-package "SB!VM")
;;; Dummy placeholder file.

View file

@ -0,0 +1,58 @@
;;;; stuff to handle simple cases for generic arithmetic
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; division
(define-assembly-routine (signed-truncate
(:note "(signed-byte 32) truncate")
(:cost 60)
(:policy :fast-safe)
(:translate truncate)
(:arg-types signed-num signed-num)
(:result-types signed-num signed-num))
((:arg dividend signed-reg nl2-offset)
(:arg divisor signed-reg nargs-offset)
(:res quo signed-reg nl2-offset)
(:res rem signed-reg nl3-offset)
(:temp quo-sign non-descriptor-reg ocfp-offset)
(:temp rem-sign descriptor-reg r8-offset))
(inst eor quo-sign dividend divisor)
(inst bic rem-sign dividend fixnum-tag-mask)
(inst mov rem 0)
(inst tst divisor divisor)
(let ((error (generate-error-code nil 'division-by-zero-error
dividend divisor)))
(inst b :eq error))
(inst rsb :mi divisor divisor 0)
(inst tst dividend dividend)
(inst rsb :mi dividend dividend 0)
(dotimes (i 33)
(inst cmp rem divisor)
(inst sub :hs rem rem divisor)
(inst adcs quo quo quo)
(unless (= i 32)
(inst adc rem rem rem)))
;; If the quo-sign is negative, we need to negate quo.
(inst tst quo-sign quo-sign)
(inst rsb :mi quo quo 0)
;; If the rem-sign is negative, we need to negate rem.
(inst tst rem-sign rem-sign)
(inst rsb :mi rem rem 0))

View file

@ -0,0 +1,91 @@
;;;; various array operations that are too expensive (in space) to do
;;;; inline
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(define-assembly-routine (allocate-vector-on-heap
(:policy :fast-safe)
(:arg-types positive-fixnum
positive-fixnum
positive-fixnum))
((:arg type any-reg r0-offset)
(:arg length any-reg r1-offset)
(:arg words any-reg r2-offset)
(:res result descriptor-reg r0-offset)
(:temp ndescr non-descriptor-reg nl2-offset)
(:temp pa-flag non-descriptor-reg ocfp-offset)
(:temp vector descriptor-reg r8-offset))
;; Why :LINK NIL?
;; Either LR or PC need to always point into the code object.
;; Since this is a static assembly routine, PC is already not pointing there.
;; But it's called using blx, so LR is still good.
;; Normally PSEUDO-ATOMIC calls do_pending_interrupt using BLX too,
;; which will make LR point here, now GC can collect the parent function away.
;; But the call to do_pending_interrupt is at the end, and there's
;; nothing more needed to be done by the routine, so
;; do_pending_interrupt can return to the parent function directly.
;; This still uses the normal :return-style, BX LR, since the call
;; to do_pending_interrupt interrupt is conditional.
(pseudo-atomic (pa-flag :link nil)
;; boxed words == unboxed bytes
(inst add ndescr words (* (1+ vector-data-offset) n-word-bytes))
(inst bic ndescr ndescr lowtag-mask)
(allocation vector ndescr other-pointer-lowtag :flag-tn pa-flag)
(inst mov ndescr (lsr type word-shift))
(storew ndescr vector 0 other-pointer-lowtag)
;; Touch the last element, to ensure that null-terminated strings
;; passed to C do not cause a WP violation in foreign code.
;; Do that before storing length, since nil-arrays don't have any
;; space, but may have non-zero length.
#!-gencgc
(inst mov ndescr 0)
#!-gencgc
(storew ndescr pa-flag -1)
(storew length vector vector-length-slot other-pointer-lowtag)
(move result vector)))
(define-assembly-routine (allocate-vector-on-stack
(:policy :fast-safe)
(:arg-types positive-fixnum
positive-fixnum
positive-fixnum))
((:arg type any-reg r0-offset)
(:arg length any-reg r1-offset)
(:arg words any-reg r2-offset)
(:res result descriptor-reg r0-offset)
(:temp ndescr non-descriptor-reg nl2-offset)
(:temp pa-flag non-descriptor-reg ocfp-offset)
(:temp vector descriptor-reg r8-offset))
;; See why :LINK NIL is needed in ALLOCATE-VECTOR-ON-HEAP above.
(pseudo-atomic (pa-flag :link nil)
;; boxed words == unboxed bytes
(inst add ndescr words (* (1+ vector-data-offset) n-word-bytes))
(inst bic ndescr ndescr lowtag-mask)
(allocation vector ndescr other-pointer-lowtag
:flag-tn pa-flag
:stack-allocate-p t)
(inst mov pa-flag (lsr type word-shift))
(storew pa-flag vector 0 other-pointer-lowtag)
;; Zero fill
(let ((loop (gen-label)))
(inst sub result vector (- other-pointer-lowtag n-word-bytes))
;; The header word has already been set, skip it.
(inst sub ndescr ndescr (fixnumize 1))
(inst mov pa-flag 0)
(emit-label loop)
(inst str pa-flag (@ result n-word-bytes :post-index))
(inst subs ndescr ndescr (fixnumize 1))
(inst b :gt loop))
(storew length vector vector-length-slot other-pointer-lowtag)
(move result vector)))

View file

@ -0,0 +1,230 @@
(in-package "SB!VM")
;;;; Return-multiple with other than one value
#+sb-assembling ;; we don't want a vop for this one.
(define-assembly-routine
(return-multiple
(:return-style :none))
;; These four are really arguments.
((:temp nvals any-reg nargs-offset)
(:temp vals any-reg ocfp-offset)
(:temp old-fp any-reg nl2-offset)
(:temp lra descriptor-reg lexenv-offset)
;; These are just needed to facilitate the transfer
(:temp count any-reg nfp-offset)
(:temp src any-reg code-offset)
(:temp dst descriptor-reg r8-offset)
;; These are needed so we can get at the register args.
(:temp r0 descriptor-reg r0-offset)
(:temp r1 descriptor-reg r1-offset)
(:temp r2 descriptor-reg r2-offset))
;; Note, because of the way the return-multiple vop is written, we
;; can assume that we are never called with nvals == 1 (not that it
;; helps overmuch).
;; If there are more return values than there are arg-passing
;; registers, then we need to arrange for the excess values to be
;; moved.
(inst cmp nvals (fixnumize 3))
(inst b :gt MOVE-STACK-VALUES)
;; We don't need to copy stack values at this point, so default any
;; unsupplied values that should be in arg-passing registers. First
;; piece of black magic: A computed jump.
(inst add pc-tn pc-tn nvals)
;; Eat a word of padding for the computed jump.
(inst word 0)
;; The computed jump above will land on one of the next four
;; instructions, based on the number of values to return.
(inst mov r0 null-tn)
(inst mov r1 null-tn)
(inst mov r2 null-tn)
;; We've defaulted any unsupplied parameters, but now we need to
;; load the supplied parameters. Second piece of black magic: A
;; hairier computed jump.
(inst rsb count nvals (fixnumize 2))
(inst add pc-tn pc-tn count)
;; The computed jump above will land on one of the next four
;; instructions, based on the number of values to return, in reverse
;; order.
(inst ldr r2 (@ vals (* 2 n-word-bytes)))
;; If we need to copy stack values, we land here so as to load the
;; first two register values (the third will be loaded after the
;; values are copied, due to register pressure).
MOVE-STACK-VALUES
(inst ldr r1 (@ vals n-word-bytes))
(inst ldr r0 (@ vals))
;; The last instruction to set the flags was the CMP to check to see
;; if we needed to move the values on the stack. If we do not need
;; to move the values on the stack then we're almost done.
(inst b :le DONE)
;; Copy the remaining args (including the future R2 register value)
;; over the outbound stack frame.
(inst add src vals (* 2 n-word-bytes))
(inst add dst cfp-tn (* 2 n-word-bytes))
(inst sub count nvals (fixnumize 2))
LOOP
(inst subs count count (fixnumize 1))
(inst ldr r2 (@ src n-word-bytes :post-index))
(inst str r2 (@ dst n-word-bytes :post-index))
(inst b :ge LOOP)
;; Load the last remaining register result.
(inst ldr r2 (@ cfp-tn (* 2 n-word-bytes)))
DONE
;; Deallocate the unused stack space.
(move ocfp-tn cfp-tn)
(move cfp-tn old-fp)
(inst add dst ocfp-tn nvals)
(store-csp dst)
;; Return.
(lisp-return lra :multiple-values))
;;;; tail-call-variable.
#+sb-assembling ;; no vop for this one either.
(define-assembly-routine
(tail-call-variable
(:return-style :none))
;; These are really args.
((:temp args any-reg nl2-offset)
(:temp lexenv descriptor-reg lexenv-offset)
;; We need to compute this
(:temp nargs any-reg nargs-offset)
;; These are needed by the blitting code.
(:temp dest any-reg nl2-offset) ;; Not live concurrent with ARGS.
(:temp count any-reg nl3-offset)
(:temp temp descriptor-reg r8-offset)
(:temp stack-top non-descriptor-reg ocfp-offset)
;; These are needed so we can get at the register args.
(:temp r0 descriptor-reg r0-offset)
(:temp r1 descriptor-reg r1-offset)
(:temp r2 descriptor-reg r2-offset))
;; We're in a tail-call scenario, so we use the existing LRA and
;; OCFP, both already set up in the stack frame. We have a set of
;; arguments, represented as the address of the first argument
;; (ARGS) and the address just beyond the last argument (CSP-TN),
;; and need to set up the arg-passing-registers (R0, R1, and R2),
;; any stack arguments (the fourth and subsequent arguments, if such
;; exist), and the total arg count (NARGS).
;; Calculate NARGS (as a fixnum)
(load-csp nargs)
(inst sub nargs nargs args)
;; Load the argument regs (must do this now, 'cause the blt might
;; trash these locations, and we need ARGS to be dead for the blt)
(loadw r0 args 0)
(loadw r1 args 1)
(loadw r2 args 2)
;; ARGS is now dead, we access the remaining arguments by offset
;; from CSP-TN.
;; Figure out how many arguments we really need to shift.
(inst subs count nargs (fixnumize register-arg-count))
;; If there aren't any stack args then we're done.
(inst b :le DONE)
;; Find where our shifted arguments ned to go.
(inst add dest cfp-tn nargs)
;; And come from.
(load-csp stack-top)
LOOP
;; Copy one arg.
(inst ldr temp (@ stack-top (- count)))
(inst str temp (@ dest (- count)))
(inst subs count count n-word-bytes)
(inst b :ne LOOP)
DONE
;; The call frame is all set up, so all that remains is to jump to
;; the new function. We need a boxed register to hold the actual
;; function object (in case of closure functions or funcallable
;; instances), and R8 (known as TEMP) and, technically, CODE happen
;; to be the only ones available.
(loadw temp lexenv closure-fun-slot fun-pointer-lowtag)
(lisp-jump temp))
;;;; Non-local exit noise.
(define-assembly-routine (throw
(:return-style :none))
((:arg target descriptor-reg r0-offset)
(:arg start any-reg r8-offset)
(:arg count any-reg nargs-offset)
(:temp catch any-reg r1-offset)
(:temp tag descriptor-reg r2-offset))
(declare (ignore start count))
(load-symbol-value catch *current-catch-block*)
LOOP
(let ((error (generate-error-code nil 'unseen-throw-tag-error target)))
(inst cmp catch 0)
(inst b :eq error))
(loadw tag catch catch-block-tag-slot)
(inst cmp tag target)
(loadw catch catch catch-block-previous-catch-slot 0 :ne)
(inst b :ne LOOP)
;; As a dreadful cleverness, make use of the fact that assembly
;; routines are emitted in order, with no padding, and that the body
;; of UNWIND follows to arrange for the stack to be unwound to our
;; chosen destination.
(move target catch) ;; TARGET coincides with UNWIND's BLOCK argument
)
(define-assembly-routine (unwind
(:return-style :none)
(:translate %continue-unwind)
(:policy :fast-safe))
((:arg block (any-reg descriptor-reg) r0-offset)
(:arg start (any-reg descriptor-reg) r8-offset)
(:arg count (any-reg descriptor-reg) nargs-offset)
(:temp ocfp non-descriptor-reg ocfp-offset)
(:temp lra descriptor-reg lexenv-offset)
(:temp cur-uwp any-reg nl2-offset))
(declare (ignore start count))
(let ((error (generate-error-code nil 'invalid-unwind-error)))
(inst cmp block 0)
(inst b :eq error))
(load-symbol-value cur-uwp *current-unwind-protect-block*)
(loadw ocfp block unwind-block-current-uwp-slot)
(inst cmp cur-uwp ocfp)
(loadw ocfp cur-uwp unwind-block-current-uwp-slot 0 :ne)
(store-symbol-value ocfp *current-unwind-protect-block* :ne)
(move cur-uwp block :eq)
(loadw cfp-tn cur-uwp unwind-block-current-cont-slot)
(loadw code-tn cur-uwp unwind-block-current-code-slot)
(loadw lra cur-uwp unwind-block-entry-pc-slot)
(lisp-return lra :known))

View file

@ -0,0 +1,80 @@
;;;; the machine-specific support routines needed by the file assembler
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(defun generate-call-sequence (name style vop options)
(declare (ignore options))
(ecase style
((:none :raw)
(let ((fixup-address (make-symbol "FIXUP-ADDRESS")))
(values
`((let ((,fixup-address (gen-label)))
,@(if (eq style :none)
`((inst load-from-label pc-tn lr-tn ,fixup-address))
`((inst load-from-label lr-tn lr-tn ,fixup-address)
(inst blx lr-tn)))
(assemble (*elsewhere* ,vop)
(emit-label ,fixup-address)
(inst word (make-fixup ',name :assembly-routine)))))
nil)))
#+(or)
(:full-call
(let ((temp (make-symbol "TEMP"))
(jump (make-symbol "JUMP"))
(nfp-save (make-symbol "NFP-SAVE"))
(lra (make-symbol "LRA")))
(values
`((let ((lra-label (gen-label))
(cur-nfp (current-nfp-tn ,vop)))
(when cur-nfp
(store-stack-tn ,nfp-save cur-nfp))
(inst compute-lra-from-code ,lra code-tn lra-label ,temp)
(note-next-instruction ,vop :call-site)
(inst lr ,jump (make-fixup ',name :assembly-routine))
(inst mtlr ,jump)
(inst blr)
(emit-return-pc lra-label)
(note-this-location ,vop :single-value-return)
(without-scheduling ()
(move csp-tn ocfp-tn)
(inst nop))
(inst compute-code-from-lra code-tn lra-tn
lra-label ,temp)
(when cur-nfp
(load-stack-tn cur-nfp ,nfp-save))))
`((:temporary (:scs (non-descriptor-reg) :from (:eval 0) :to (:eval 1))
,temp)
(:temporary (:sc descriptor-reg :offset lra-offset
:from (:eval 0) :to (:eval 1))
,lra)
(:temporary (:scs (control-stack) :offset nfp-save-offset)
,nfp-save)
(:temporary (:sc any-reg) ,jump)
(:save-p :compute-only)))))))
(defun generate-return-sequence (style)
(ecase style
(:raw
`((inst bx lr-tn)))
#+(or)
(:full-call
`((lisp-return (make-random-tn :kind :normal
:sc (sc-or-lose 'descriptor-reg )
:offset lra-offset)
(make-random-tn :kind :normal
:sc (sc-or-lose 'interior-reg )
:offset lip-offset)
:offset 2)))
(:none)))
(defun return-machine-address (scp)
(context-register scp lr-offset))

7
src/bootstrap.lisp Normal file
View file

@ -0,0 +1,7 @@
(in-package "SB!KERNEL")
(defun bar (x)
(values x #. (coerce "Hello World" 'simple-base-string)))
(defun !cold-init ()
(cons 20 30))

547
src/bootstrap.trace Normal file
View file

@ -0,0 +1,547 @@
;;;; component: "top level form"
IR1 block 0 start c1
start stack:
1> bind SB!C::CLAMBDA (SB!C::TOP-LEVEL-FORM NIL) :KIND :TOPLEVEL
2> 3: FIND-UNDELETED-PACKAGE-OR-LOSE {GLOBAL-FUNCTION}
4> 5: '"SB!KERNEL"
6> 7: known combination v3 v5
8> set *PACKAGE* {SPECIAL} v7
9> 10: 'NIL
11> return v10 SB!C::CLAMBDA (SB!C::TOP-LEVEL-FORM NIL)
end stack:
successors c12
;;;; IR2 component: "top level form"
entries:
L1: (SB!C::TOP-LEVEL-FORM NIL)
TNs: 17 local, 12 temps, 4 constant, 2 env, 0 comp, 2 global.
Wired: 21, Unused: 0. 1 block, 4 global conflicts.
IR1 block 3 start c12
IR2 block NIL start c12
IR1 block 0 start c1
IR2 block 0 start c1
0: XEP-ALLOCATE-FRAME {#<SB!ASSEM:LABEL 1>}
1: XEP-SETUP-SP
2: MOVE t1[CS0]>t2[NL0] => t3[CS0]<t4[NL0]
3: MOVE t5[CS1]>t6[R0] => t7[CS1]<t8[R0]
4: NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 2>}
5: ALLOCATE-FULL-CALL-FRAME {1} => t9[OCFP]
6: MOVE-ARG '"SB!KERNEL"!10[Const4]>t11[R0] t9[OCFP] => t12[R0]
7: CALL-NAMED t9[OCFP] t13[Const5] t12[R0] {# 1 1 NIL} => t14[R0]
8: MOVE t14[R0] => t15[R0]
9: SET '*PACKAGE*!16[Const6]>t17[R1] t15[R0]
10: MOVE 'NIL!18 => t19[R0]
11: RETURN-SINGLE t3[CS0]>t20[NL0] t7[CS1]>t21[R1] t19[R0]
IR1 block 1 start c12
IR2 block NIL start c12
assembly code for #<SB!C:COMPONENT :NAME "top level form" {1014292303}>
in the ELSEWHERE segment:
L3:
in the REGULAR segment:
L4:
in the ELSEWHERE segment:
L5:
in the REGULAR segment:
VOP XEP-ALLOCATE-FRAME {#<SB!ASSEM:LABEL 1>}
.align 4
L1:
SIMPLE-FUN-HEADER-WORD
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
COMPUTE-CODE #<TN t22[CODE]>, #<TN t23[LR]>, L1, #<TN t24[NL0]>
VOP XEP-SETUP-SP
ADD #<TN t25[CSP]>, #<TN t26[CFP]>, 24
VOP NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 2>}
L2:
VOP ALLOCATE-FULL-CALL-FRAME {1} => t9[OCFP]
ORR #<TN t9[OCFP]>, #<TN t27[ZR]>, #<TN t25[CSP]>
ADD #<TN t25[CSP]>, #<TN t25[CSP]>, 16
STR #<TN t26[CFP]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t9[OCFP]>
:OFFSET 0
:MODE OFFSET)
VOP MOVE-ARG '"SB!KERNEL"!10[Const4]>t11[R0] t9[OCFP] => t12[R0]
LDR #<TN t11[R0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t22[CODE]>
:OFFSET 17
:MODE OFFSET)
VOP CALL-NAMED t9[OCFP] t13[Const5] t12[R0] {# 1 1 NIL} => t14[R0]
LDR #<TN t28[LEXENV]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t22[CODE]>
:OFFSET 25
:MODE OFFSET)
MOVZ #<TN t29[NARGS]>, 2
LDR #<TN t30[R1]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t28[LEXENV]>
:OFFSET 9
:MODE OFFSET)
COMPUTE-LRA #<TN t31[LR]>, #<TN t31[LR]>, L6
STR #<TN t31[LR]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t9[OCFP]>
:OFFSET 8
:MODE OFFSET)
ORR #<TN t26[CFP]>, #<TN t27[ZR]>, #<TN t9[OCFP]>
L7:
ADD #<TN t32[TMP]>, #<TN t30[R1]>, 45
BR #<TN t32[TMP]>
.align 4
L6:
LRA-HEADER-WORD
L8:
COMPUTE-CODE #<TN t22[CODE]>, #<TN t31[LR]>, L6, #<TN t33[NL0]>
CSEL #<TN t25[CSP]>, #<TN t34[OCFP]>, #<TN t25[CSP]>, EQ
VOP SET '*PACKAGE*!16[Const6]>t17[R1] t15[R0]
LDR #<TN t17[R1]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t22[CODE]>
:OFFSET 33
:MODE OFFSET)
STR #<TN t15[R0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t17[R1]>
:OFFSET -7
:MODE OFFSET)
VOP MOVE 'NIL!18 => t19[R0]
ORR #<TN t19[R0]>, #<TN t27[ZR]>, #<TN t35[NULL]>
VOP RETURN-SINGLE t3[CS0]>t20[NL0] t7[CS1]>t21[R1] t19[R0]
LDR #<TN t20[NL0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t26[CFP]>
:OFFSET 0
:MODE OFFSET)
LDR #<TN t21[R1]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t26[CFP]>
:OFFSET 8
:MODE OFFSET)
ORR #<TN t25[CSP]>, #<TN t27[ZR]>, #<TN t26[CFP]>
ORR #<TN t26[CFP]>, #<TN t27[ZR]>, #<TN t20[NL0]>
MSR NZCV, #<TN t27[ZR]>
SUB #<TN t32[TMP]>, #<TN t21[R1]>, 7
BR #<TN t32[TMP]>
.align 4
;;;; component: BAR
IR1 block 0 start c13
start stack:
13> bind SB!C::CLAMBDA (SB!C::TL-XEP BAR) :KIND :EXTERNAL
14> 15: SB!C::CLAMBDA BAR
16> 17: #:G0
18> tail local combination v15 v17
end stack:
successors c19
IR1 block 1 start c19
start stack:
19> bind SB!C::CLAMBDA BAR
end stack:
successors c20
IR1 block 2 start c20
start stack:
20> entry NIL
21> 22: VALUES {GLOBAL-FUNCTION}
23> 24: X
25> 26: '"Hello World"
27> 28: known combination v22 v24 v26
end stack:
successors c29
IR1 block 3 start c29
start stack:
29> return v28 SB!C::CLAMBDA BAR
end stack:
successors c12
;;;; IR2 component: BAR
entries:
L9: BAR
TNs: 12 local, 10 temps, 1 constant, 6 env, 0 comp, 5 global.
Wired: 23, Unused: 0. 4 blocks, 20 global conflicts.
IR1 block 6 start c12
IR2 block NIL start c12
IR1 block 0 start c13
IR2 block 3 start c13
0: XEP-ALLOCATE-FRAME {#<SB!ASSEM:LABEL 9>}
1: VERIFY-ARG-COUNT t36[NARGS] {NIL 1}
2: XEP-SETUP-SP
3: MOVE t37[R0] => #:G0!38[R0]
4: MOVE t39[CS0]>t40[NL0] => t41[CS0]<t42[NL0]
5: MOVE t43[CS1]>t44[R1] => t45[CS1]<t46[R1]
6: NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 10>}
7: MOVE #:G0!38[R0] => X!47[R0]
8: MOVE t41[CS0]>t48[NL0] => t49[CS0]<t50[NL0]
9: MOVE t45[CS1]>t51[R1] => t52[CS1]<t53[R1]
IR1 block 1 start c19
IR2 block 2 start c19
0: MOVE t52[CS1]>t54[R1] => t55[CS1]<t56[R1]
1: NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 11>}
IR1 block 2 start c20
IR2 block 1 start c20
IR1 block 3 start c29
IR2 block 0 start c29
0: MOVE X!47[R0] => t57[R0]
1: MOVE '"Hello World"!58[Const4]>t59[R1] => t60[R1]
2: RETURN t49[CS0]>t61[NL0] t55[CS1]>t62[LEXENV] t57[R0] t60[R1] {2}
IR1 block 1 start c12
IR2 block NIL start c12
assembly code for #<SB!C:COMPONENT :NAME BAR {10142DF753}>
in the ELSEWHERE segment:
L12:
in the REGULAR segment:
L13:
in the ELSEWHERE segment:
L14:
in the REGULAR segment:
VOP XEP-ALLOCATE-FRAME {#<SB!ASSEM:LABEL 9>}
.align 4
L9:
SIMPLE-FUN-HEADER-WORD
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
COMPUTE-CODE #<TN t22[CODE]>, #<TN t63[LR]>, L9, #<TN t64[NL2]>
in the ELSEWHERE segment:
VOP VERIFY-ARG-COUNT t36[NARGS] {NIL 1}
L15:
L16:
DEBUG-TRAP
BYTE 10
BYTE 4
BYTE 25
BYTE 254
BYTE 132
BYTE 5
.align 3
in the REGULAR segment:
SUBS #<TN t27[ZR]>, #<TN t36[NARGS]>, 2
B NE, L15
VOP XEP-SETUP-SP
ADD #<TN t25[CSP]>, #<TN t26[CFP]>, 16
VOP NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 10>}
L10:
L17:
in the ELSEWHERE segment:
L18:
in the REGULAR segment:
VOP NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 11>}
L11:
L19:
L20:
VOP MOVE '"Hello World"!58[Const4]>t59[R1] => t60[R1]
LDR #<TN t59[R1]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t22[CODE]>
:OFFSET 17
:MODE OFFSET)
VOP RETURN t49[CS0]>t61[NL0] t55[CS1]>t62[LEXENV] t57[R0] t60[R1] {2}
LDR #<TN t61[NL0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t26[CFP]>
:OFFSET 0
:MODE OFFSET)
LDR #<TN t62[LEXENV]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t26[CFP]>
:OFFSET 8
:MODE OFFSET)
ORR #<TN t65[OCFP]>, #<TN t27[ZR]>, #<TN t26[CFP]>
ORR #<TN t26[CFP]>, #<TN t27[ZR]>, #<TN t61[NL0]>
ADD #<TN t66[NARGS]>, #<TN t65[OCFP]>, 16
ORR #<TN t25[CSP]>, #<TN t27[ZR]>, #<TN t66[NARGS]>
MOVZ #<TN t66[NARGS]>, 4
ORR #<TN t67[R2]>, #<TN t27[ZR]>, #<TN t35[NULL]>
ORR #<TN t32[TMP]>, #<TN t27[ZR]>, 4026531840
MSR NZCV, #<TN t32[TMP]>
SUB #<TN t32[TMP]>, #<TN t68[LEXENV]>, 7
BR #<TN t32[TMP]>
.align 4
;;;; component: !COLD-INIT
IR1 block 0 start c30
start stack:
30> bind SB!C::CLAMBDA (SB!C::TL-XEP !COLD-INIT) :KIND :EXTERNAL
31> 32: SB!C::CLAMBDA !COLD-INIT
33> tail local combination v32
end stack:
successors c34
IR1 block 1 start c34
start stack:
34> bind SB!C::CLAMBDA !COLD-INIT
end stack:
successors c35
IR1 block 2 start c35
start stack:
35> entry NIL
36> 37: CONS {GLOBAL-FUNCTION}
38> 39: '20
40> 41: '30
42> 43: known combination v37 v39 v41
end stack:
successors c44
IR1 block 3 start c44
start stack:
44> return v43 SB!C::CLAMBDA !COLD-INIT
end stack:
successors c12
;;;; IR2 component: !COLD-INIT
entries:
L21: !COLD-INIT
TNs: 6 local, 5 temps, 2 constant, 4 env, 0 comp, 5 global.
Wired: 15, Unused: 0. 4 blocks, 16 global conflicts.
IR1 block 6 start c12
IR2 block NIL start c12
IR1 block 0 start c30
IR2 block 3 start c30
0: XEP-ALLOCATE-FRAME {#<SB!ASSEM:LABEL 21>}
1: VERIFY-ARG-COUNT t69[NARGS] {NIL 0}
2: XEP-SETUP-SP
3: MOVE t70[CS0]>t71[NL0] => t72[CS0]<t73[NL0]
4: MOVE t74[CS1]>t75[R0] => t76[CS1]<t77[R0]
5: NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 22>}
6: MOVE t72[CS0]>t78[NL0] => t79[CS0]<t80[NL0]
7: MOVE t76[CS1]>t81[R0] => t82[CS1]<t83[R0]
IR1 block 1 start c34
IR2 block 2 start c34
0: MOVE t82[CS1]>t84[R0] => t85[CS1]<t86[R0]
1: NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 23>}
IR1 block 2 start c35
IR2 block 1 start c35
0: FIXED-ALLOC {CONS 2 NIL 7 NIL} => t87[R0]
1: INIT-SLOT t87[R0] '20!88>t89[NL0] {CONS 0 7}
2: INIT-SLOT t87[R0] '30!90>t91[NL0] {CONS 1 7}
IR1 block 3 start c44
IR2 block 0 start c44
0: MOVE t87[R0] => t92[R0]
1: RETURN-SINGLE t79[CS0]>t93[NL0] t85[CS1]>t94[R1] t92[R0]
IR1 block 1 start c12
IR2 block NIL start c12
assembly code for #<SB!C:COMPONENT :NAME !COLD-INIT {1014327383}>
in the ELSEWHERE segment:
L24:
in the REGULAR segment:
L25:
in the ELSEWHERE segment:
L26:
in the REGULAR segment:
VOP XEP-ALLOCATE-FRAME {#<SB!ASSEM:LABEL 21>}
.align 4
L21:
SIMPLE-FUN-HEADER-WORD
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
COMPUTE-CODE #<TN t22[CODE]>, #<TN t95[LR]>, L21, #<TN t96[NL2]>
in the ELSEWHERE segment:
VOP VERIFY-ARG-COUNT t69[NARGS] {NIL 0}
L27:
L28:
DEBUG-TRAP
BYTE 10
BYTE 4
BYTE 25
BYTE 254
BYTE 132
BYTE 5
.align 3
in the REGULAR segment:
SUBS #<TN t27[ZR]>, #<TN t69[NARGS]>, 0
B NE, L27
VOP XEP-SETUP-SP
ADD #<TN t25[CSP]>, #<TN t26[CFP]>, 16
VOP NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 22>}
L22:
L29:
in the ELSEWHERE segment:
L30:
in the REGULAR segment:
VOP NOTE-ENVIRONMENT-START {#<SB!ASSEM:LABEL 23>}
L23:
L31:
VOP FIXED-ALLOC {CONS 2 NIL 7 NIL} => t87[R0]
MOVZ #<TN t32[TMP]>, 2209
STR #<TN t25[CSP]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t35[NULL]>
:OFFSET #<TN t32[TMP]>
:MODE OFFSET)
LOAD-FROM-LABEL #<TN t97[OCFP]>, L32
LDR #<TN t87[R0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t97[OCFP]>
:OFFSET 0
:MODE OFFSET)
LDR #<TN t97[OCFP]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t97[OCFP]>
:OFFSET 8
:MODE OFFSET)
ADD #<TN t87[R0]>, #<TN t87[R0]>, 16
SUBS #<TN t27[ZR]>, #<TN t87[R0]>, #<TN t97[OCFP]>
B HI, L33
LOAD-FROM-LABEL #<TN t97[OCFP]>, L32
STR #<TN t87[R0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t97[OCFP]>
:OFFSET 0
:MODE OFFSET)
SUB #<TN t87[R0]>, #<TN t87[R0]>, 16
L34:
ADD #<TN t87[R0]>, #<TN t87[R0]>, 7
in the ELSEWHERE segment:
L33:
MOVZ #<TN t87[R0]>, 16
STP #<TN t87[R0]>, #<TN t98[LR]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t99[ZR]>
:OFFSET -16
:MODE PRE-INDEX)
LOAD-FROM-LABEL #<TN t87[R0]>, L35
BLR #<TN t87[R0]>
LDP #<TN t87[R0]>, #<TN t98[LR]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t99[ZR]>
:OFFSET 16
:MODE POST-INDEX)
B L34
L35:
DWORD #S(SB!C:FIXUP :NAME alloc_tramp :FLAVOR FOREIGN :OFFSET NIL)
L32:
DWORD #S(SB!C:FIXUP :NAME boxed_region :FLAVOR FOREIGN :OFFSET NIL)
in the REGULAR segment:
MOVZ #<TN t32[TMP]>, 2209
STR #<TN t35[NULL]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t35[NULL]>
:OFFSET #<TN t32[TMP]>
:MODE OFFSET)
MOVZ #<TN t32[TMP]>, 2257
LDR #<TN t97[OCFP]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t35[NULL]>
:OFFSET #<TN t32[TMP]>
:MODE OFFSET)
CBZ #<TN t97[OCFP]>, L36
BLR #<TN t97[OCFP]>
L36:
VOP INIT-SLOT t87[R0] '20!88>t89[NL0] {CONS 0 7}
MOVZ #<TN t89[NL0]>, 40
STR #<TN t89[NL0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t87[R0]>
:OFFSET -7
:MODE OFFSET)
VOP INIT-SLOT t87[R0] '30!90>t91[NL0] {CONS 1 7}
MOVZ #<TN t91[NL0]>, 60
STR #<TN t91[NL0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t87[R0]>
:OFFSET 1
:MODE OFFSET)
L37:
VOP RETURN-SINGLE t79[CS0]>t93[NL0] t85[CS1]>t94[R1] t92[R0]
LDR #<TN t93[NL0]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t26[CFP]>
:OFFSET 0
:MODE OFFSET)
LDR #<TN t94[R1]>, #S(SB!VM::MEMORY-OPERAND
:BASE #<TN t26[CFP]>
:OFFSET 8
:MODE OFFSET)
ORR #<TN t25[CSP]>, #<TN t27[ZR]>, #<TN t26[CFP]>
ORR #<TN t26[CFP]>, #<TN t27[ZR]>, #<TN t93[NL0]>
MSR NZCV, #<TN t27[ZR]>
SUB #<TN t32[TMP]>, #<TN t94[R1]>, 7
BR #<TN t32[TMP]>
.align 4

70
src/code/arm64-vm.lisp Normal file
View file

@ -0,0 +1,70 @@
;;; This file contains the ARM specific runtime stuff.
;;;
(in-package "SB!VM")
;;; See x86-vm.lisp for a description of this.
;;; FIXME: Why is this present in every ARCH-vm.lisp with the the same definition. Is there something like common-vm?
(define-alien-type os-context-t (struct os-context-t-struct))
(defun machine-type ()
#!+sb-doc
"Return a string describing the type of the local machine."
"ARM64")
;;;; FIXUP-CODE-OBJECT
(defun fixup-code-object (code offset fixup kind)
(declare (type index offset))
(unless (zerop (rem offset n-word-bytes))
(error "Unaligned instruction? offset=#x~X." offset))
(sb!sys:without-gcing
(let ((sap (%primitive sb!kernel::code-instructions code)))
(ecase kind
(:absolute
(setf (sap-ref-32 sap offset) fixup))))))
;;;; "Sigcontext" access functions, cut & pasted from sparc-vm.lisp,
;;;; then modified for ARM.
;;;;
;;;; See also x86-vm for commentary on signed vs unsigned.
(define-alien-routine ("os_context_register_addr" context-register-addr)
(* unsigned-int)
(context (* os-context-t))
(index int))
;;; FIXME: Should this and CONTEXT-PC be INLINE to reduce consing?
;;; (Are they used in anything time-critical, or just the debugger?)
(defun context-register (context index)
(declare (type (alien (* os-context-t)) context))
(deref (context-register-addr context index)))
(defun %set-context-register (context index new)
(declare (type (alien (* os-context-t)) context))
(setf (deref (context-register-addr context index))
new))
(defun context-pc (context)
(int-sap (context-register context pc-offset)))
;;;; INTERNAL-ERROR-ARGS.
;;; Given a (POSIX) signal context, extract the internal error
;;; arguments from the instruction stream.
(defun internal-error-args (context)
(declare (type (alien (* os-context-t)) context))
(let* ((pc (context-pc context))
(length (sap-ref-8 pc 5)) ;; Skip trap instruction and kind byte
(vector (make-array length :element-type '(unsigned-byte 8))))
(declare (type system-area-pointer pc)
(type (unsigned-byte 8) length)
(type (simple-array (unsigned-byte 8) (*)) vector))
(copy-ub8-from-system-area pc 6 vector 0 length)
(let* ((index 0)
(error-number (sb!c:read-var-integer vector index)))
(collect ((sc-offsets))
(loop
(when (>= index length)
(return))
(sc-offsets (sb!c:read-var-integer vector index)))
(values error-number (sc-offsets))))))

View file

@ -170,7 +170,7 @@
(def-type-predicate-wrapper vectorp)
(def-type-predicate-wrapper vector-nil-p))
#!+(or x86 x86-64 arm)
#!+(or x86 x86-64 arm arm64)
(defun fixnum-mod-p (x limit)
(and (fixnump x)
(<= 0 x limit)))

View file

@ -142,7 +142,7 @@
"target backend-subfeatures *SHEBANG-BACKEND-FEATURES*=~@<~S~:>~%"
*shebang-backend-subfeatures*))
(let ((arch (intersection '(:alpha :arm :hppa :mips :ppc :sparc :x86 :x86-64)
(let ((arch (intersection '(:alpha :arm :arm64 :hppa :mips :ppc :sparc :x86 :x86-64)
*shebang-features*)))
(cond ((not arch) (error "No architecture selected"))
((> (length arch) 1) (error "More than one architecture selected"))))
@ -160,7 +160,7 @@
":GENCGC and :CHENEYGC are incompatible")
("(and cheneygc (not (or alpha arm hppa mips ppc sparc)))"
":CHENEYGC not supported on selected architecture")
("(and gencgc (not (or sparc ppc x86 x86-64 arm)))"
("(and gencgc (not (or sparc ppc x86 x86-64 arm arm64)))"
":GENCGC not supported on selected architecture")
("(not (or gencgc cheneygc))"
"One of :GENCGC or :CHENEYGC must be enabled")
@ -260,6 +260,7 @@
#!+alpha "alpha"
#!+hppa "hppa"
#!+arm "arm"
#!+arm64 "arm64"
(subseq stem (+ position 7)))
stem)))
(compile 'stem-remap-target)

View file

@ -0,0 +1,200 @@
;;;; allocation VOPs for the ARM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(define-vop (list-or-list*)
(:args (things :more t :scs (control-stack)))
(:temporary (:scs (descriptor-reg) :type list) ptr)
(:temporary (:scs (any-reg)) temp)
(:temporary (:scs (descriptor-reg) :type list :to (:result 0) :target result)
res)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:info num)
(:results (result :scs (descriptor-reg)))
(:variant-vars star)
(:policy :fast-safe)
(:node-var node)
(:generator 0
(cond ((zerop num)
(move result null-tn))
((and star (= num 1))
(move result (tn-ref-tn things)))
(t
(macrolet
((maybe-load (tn)
(once-only ((tn tn))
`(sc-case ,tn
((any-reg descriptor-reg null)
,tn)
(control-stack
(load-stack-tn temp ,tn)
temp)))))
(let* ((cons-cells (if star (1- num) num))
(alloc (* (pad-data-block cons-size) cons-cells)))
(pseudo-atomic (pa-flag)
(allocation res alloc list-pointer-lowtag
:flag-tn pa-flag
:stack-allocate-p (node-stack-allocate-p node))
(move ptr res)
(dotimes (i (1- cons-cells))
(storew (maybe-load (tn-ref-tn things)) ptr
cons-car-slot list-pointer-lowtag)
(setf things (tn-ref-across things))
(inst add ptr ptr (pad-data-block cons-size))
(storew ptr ptr
(- cons-cdr-slot cons-size)
list-pointer-lowtag))
(storew (maybe-load (tn-ref-tn things)) ptr
cons-car-slot list-pointer-lowtag)
(storew (if star
(maybe-load (tn-ref-tn (tn-ref-across things)))
null-tn)
ptr cons-cdr-slot list-pointer-lowtag))
(move result res)))))))
(define-vop (list list-or-list*)
(:variant nil))
(define-vop (list* list-or-list*)
(:variant t))
;;;; Special purpose inline allocators.
#!-gencgc
(define-vop (allocate-code-object)
(:args (boxed-arg :scs (any-reg))
(unboxed-arg :scs (any-reg)))
(:results (result :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) ndescr)
(:temporary (:scs (non-descriptor-reg)) size)
(:temporary (:scs (any-reg) :from (:argument 0)) boxed)
(:temporary (:scs (non-descriptor-reg)) unboxed)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:generator 100
(inst add boxed boxed-arg (fixnumize (1+ code-constants-offset)))
(inst bic boxed boxed lowtag-mask)
(inst mov unboxed (lsr unboxed-arg word-shift))
(inst add unboxed unboxed lowtag-mask)
(inst bic unboxed unboxed lowtag-mask)
(inst mov ndescr (lsl boxed (- n-widetag-bits word-shift)))
(inst orr ndescr ndescr code-header-widetag)
(inst add size boxed unboxed)
(pseudo-atomic (pa-flag)
(allocation result size other-pointer-lowtag :flag-tn pa-flag)
(storew ndescr result 0 other-pointer-lowtag)
(storew unboxed-arg result code-code-size-slot other-pointer-lowtag)
(storew null-tn result code-entry-points-slot other-pointer-lowtag)
(storew null-tn result code-debug-info-slot other-pointer-lowtag))))
(define-vop (make-fdefn)
(:args (name :scs (descriptor-reg) :to :eval))
(:temporary (:scs (non-descriptor-reg)) temp)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:results (result :scs (descriptor-reg) :from :argument))
(:temporary (:sc interior-reg) lip)
(:policy :fast-safe)
(:translate make-fdefn)
(:generator 37
(let ((undefined-tramp-fixup (gen-label)))
(with-fixed-allocation (result pa-flag fdefn-widetag fdefn-size)
(inst load-from-label temp lip undefined-tramp-fixup)
(storew name result fdefn-name-slot other-pointer-lowtag)
(storew null-tn result fdefn-fun-slot other-pointer-lowtag)
(storew temp result fdefn-raw-addr-slot other-pointer-lowtag))
(assemble (*elsewhere*)
(emit-label undefined-tramp-fixup)
(inst word (make-fixup "undefined_tramp" :foreign))))))
(define-vop (make-closure)
(:args (function :to :save :scs (descriptor-reg)))
(:info length stack-allocate-p)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:results (result :scs (descriptor-reg)))
(:generator 10
(let* ((size (+ length closure-info-offset))
(alloc-size (pad-data-block size)))
(pseudo-atomic (pa-flag)
(allocation result alloc-size
fun-pointer-lowtag
:flag-tn pa-flag
:stack-allocate-p stack-allocate-p)
(load-immediate-word pa-flag
(logior
(ash (1- size) n-widetag-bits)
closure-header-widetag))
(storew pa-flag result 0 fun-pointer-lowtag)
(storew function result closure-fun-slot fun-pointer-lowtag)))))
;;; The compiler likes to be able to directly make value cells.
;;;
(define-vop (make-value-cell)
(:args (value :to :save :scs (descriptor-reg any-reg)))
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:info stack-allocate-p)
(:results (result :scs (descriptor-reg)))
(:generator 10
(with-fixed-allocation (result pa-flag value-cell-header-widetag
value-cell-size :stack-allocate-p stack-allocate-p)
(storew value result value-cell-value-slot other-pointer-lowtag))))
;;;; Automatic allocators for primitive objects.
(define-vop (make-unbound-marker)
(:args)
(:results (result :scs (descriptor-reg any-reg)))
(:generator 1
(inst mov result unbound-marker-widetag)))
(define-vop (make-funcallable-instance-tramp)
(:args)
(:results (result :scs (any-reg)))
(:temporary (:sc interior-reg) lip)
(:generator 1
(let ((fixup (gen-label)))
(inst load-from-label result lip fixup)
(assemble (*elsewhere*)
(emit-label fixup)
(inst word (make-fixup "funcallable_instance_tramp" :foreign))))))
(define-vop (fixed-alloc)
(:args)
(:info name words type lowtag stack-allocate-p)
(:ignore name)
(:results (result :scs (descriptor-reg)))
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:generator 4
(with-fixed-allocation (result pa-flag type words
:lowtag lowtag
:stack-allocate-p stack-allocate-p))))
(define-vop (var-alloc)
(:args (extra :scs (any-reg)))
(:arg-types positive-fixnum)
(:info name words type lowtag)
(:ignore name)
(:results (result :scs (descriptor-reg)))
(:temporary (:scs (any-reg)) bytes)
(:temporary (:scs (non-descriptor-reg)) header)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:generator 6
;; Build the object header, assuming that the header was in WORDS
;; but should not be in the header
(inst add bytes extra (* (1- words) n-word-bytes))
(inst mov header (lsl bytes (- n-widetag-bits n-fixnum-tag-bits)))
(inst add header header type)
;; Add the object header to the allocation size and round up to
;; the allocation granularity
(inst add bytes bytes (* 2 n-word-bytes))
(inst bic bytes bytes lowtag-mask)
;; Allocate the object and set its header
(pseudo-atomic (pa-flag)
(allocation result bytes lowtag :flag-tn pa-flag)
(storew header result 0 lowtag))))

View file

@ -0,0 +1,976 @@
;;;; the VM definition arithmetic VOPs for the ARM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; unary operations.
(define-vop (fast-safe-arith-op)
(:policy :fast-safe)
(:effects)
(:affected))
(define-vop (fixnum-unop fast-safe-arith-op)
(:args (x :scs (any-reg)))
(:results (res :scs (any-reg)))
(:note "inline fixnum arithmetic")
(:arg-types tagged-num)
(:result-types tagged-num))
(define-vop (signed-unop fast-safe-arith-op)
(:args (x :scs (signed-reg)))
(:results (res :scs (signed-reg)))
(:note "inline (signed-byte 32) arithmetic")
(:arg-types signed-num)
(:result-types signed-num))
(define-vop (unsigned-unop fast-safe-arith-op)
(:args (x :scs (unsigned-reg)))
(:results (res :scs (unsigned-reg)))
(:note "inline (unsigned-byte 32) arithmetic")
(:arg-types unsigned-num)
(:result-types unsigned-num))
(define-vop (fast-negate/fixnum fixnum-unop)
(:translate %negate)
(:generator 1
(inst rsb res x 0)))
(define-vop (fast-negate/signed signed-unop)
(:translate %negate)
(:generator 2
(inst rsb res x 0)))
(define-vop (fast-negate/unsigned signed-unop)
(:args (x :scs (unsigned-reg) :target res))
(:arg-types unsigned-num)
(:translate %negate)
(:generator 3
(inst rsb res x 0)))
(define-vop (fast-lognot/fixnum signed-unop)
(:args (x :scs (any-reg)))
(:arg-types tagged-num)
(:translate lognot)
(:generator 1
(inst mvn res (asr x n-fixnum-tag-bits))))
(define-vop (fast-lognot/signed signed-unop)
(:translate lognot)
(:generator 2
(inst mvn res x)))
;;;; Binary fixnum operations.
;;; Assume that any constant operand is the second arg...
(define-vop (fast-fixnum-binop fast-safe-arith-op)
(:args (x :target r :scs (any-reg))
(y :target r :scs (any-reg)))
(:arg-types tagged-num tagged-num)
(:results (r :scs (any-reg)))
(:result-types tagged-num)
(:note "inline fixnum arithmetic"))
(define-vop (fast-unsigned-binop fast-safe-arith-op)
(:args (x :target r :scs (unsigned-reg))
(y :target r :scs (unsigned-reg)))
(:arg-types unsigned-num unsigned-num)
(:results (r :scs (unsigned-reg)))
(:result-types unsigned-num)
(:note "inline (unsigned-byte 32) arithmetic"))
(define-vop (fast-signed-binop fast-safe-arith-op)
(:args (x :target r :scs (signed-reg))
(y :target r :scs (signed-reg)))
(:arg-types signed-num signed-num)
(:results (r :scs (signed-reg)))
(:result-types signed-num)
(:note "inline (signed-byte 32) arithmetic"))
(define-vop (fast-fixnum-binop-c fast-safe-arith-op)
(:args (x :target r :scs (any-reg)))
(:info y)
(:arg-types tagged-num
(:constant (signed-byte #.n-fixnum-bits)))
(:results (r :scs (any-reg)))
(:result-types tagged-num)
(:note "inline fixnum arithmetic"))
(define-vop (fast-unsigned-binop-c fast-safe-arith-op)
(:args (x :target r :scs (unsigned-reg)))
(:info y)
(:arg-types unsigned-num
(:constant (unsigned-byte 32)))
(:results (r :scs (unsigned-reg)))
(:result-types unsigned-num)
(:note "inline (unsigned-byte 32) arithmetic"))
(define-vop (fast-signed-binop-c fast-safe-arith-op)
(:args (x :target r :scs (signed-reg)))
(:info y)
(:arg-types signed-num
(:constant (signed-byte 32)))
(:results (r :scs (signed-reg)))
(:result-types signed-num)
(:note "inline (signed-byte 32) arithmetic"))
(defmacro define-binop (translate untagged-penalty op
&key cop arg-swap neg-op invert-y invert-r try-single-op)
(let ((cop (or cop op)))
`(progn
(define-vop (,(symbolicate 'fast- translate '/fixnum=>fixnum)
fast-fixnum-binop)
(:translate ,translate)
(:generator 2
,(if arg-swap
`(inst ,op r y x)
`(inst ,op r x y))))
(define-vop (,(symbolicate 'fast- translate '-c/fixnum=>fixnum)
fast-fixnum-binop-c)
(:translate ,translate)
(:generator 1
(composite-immediate-instruction ,cop r x y :fixnumize t :neg-op ,neg-op :invert-y ,invert-y :invert-r ,invert-r :single-op-op ,(when try-single-op op))))
(define-vop (,(symbolicate 'fast- translate '/signed=>signed)
fast-signed-binop)
(:translate ,translate)
(:generator ,(1+ untagged-penalty)
,(if arg-swap
`(inst ,op r y x)
`(inst ,op r x y))))
(define-vop (,(symbolicate 'fast- translate '-c/signed=>signed)
fast-signed-binop-c)
(:translate ,translate)
(:generator ,untagged-penalty
(composite-immediate-instruction ,cop r x y :neg-op ,neg-op :invert-y ,invert-y :invert-r ,invert-r :single-op-op ,(when try-single-op op))))
(define-vop (,(symbolicate 'fast- translate '/unsigned=>unsigned)
fast-unsigned-binop)
(:translate ,translate)
(:generator ,(1+ untagged-penalty)
,(if arg-swap
`(inst ,op r y x)
`(inst ,op r x y))))
(define-vop (,(symbolicate 'fast- translate '-c/unsigned=>unsigned)
fast-unsigned-binop-c)
(:translate ,translate)
(:generator ,untagged-penalty
(composite-immediate-instruction ,cop r x y :neg-op ,neg-op :invert-y ,invert-y :invert-r ,invert-r :single-op-op ,(when try-single-op op)))))))
(define-binop + 4 add :neg-op sub)
(define-binop - 4 sub :neg-op add)
(define-binop logand 2 and :cop bic :invert-y t :try-single-op t)
(define-binop logandc1 2 bic :cop orr :arg-swap t :invert-y t :invert-r t)
(define-binop logandc2 2 bic)
(define-binop logior 2 orr)
(define-binop logxor 2 eor)
(define-vop (fast-logior-unsigned-signed=>signed fast-safe-arith-op)
(:args (x :scs (unsigned-reg))
(y :target r :scs (signed-reg)))
(:arg-types unsigned-num signed-num)
(:results (r :scs (signed-reg) :from (:argument 1)))
(:result-types signed-num)
(:note "inline (unsigned-byte 32) arithmetic")
(:translate logior)
(:generator 3
(inst orr r x y)))
(define-vop (fast-logior-signed-unsigned=>signed fast-safe-arith-op)
(:args (x :target r :scs (signed-reg))
(y :scs (unsigned-reg)))
(:arg-types signed-num unsigned-num)
(:results (r :scs (signed-reg) :from (:argument 0)))
(:result-types signed-num)
(:note "inline (unsigned-byte 32) arithmetic")
(:translate logior)
(:generator 3
(inst orr r x y)))
;;; Multiplication
(define-vop (fast-*/fixnum=>fixnum fast-fixnum-binop)
(:args (x :scs (signed-reg)) ;; one operand needs to be untagged
(y :target r :scs (any-reg)))
(:translate *)
(:generator 2
(inst mul r x y)))
(define-vop (fast-*-c/fixnum=>fixnum fast-fixnum-binop-c)
(:args (x :scs (any-reg) :to :result))
(:results (r :scs (any-reg) :from :eval))
(:temporary (:sc non-descriptor-reg :target r) temp)
(:translate *)
(:generator 1
(load-immediate-word temp y)
(inst mul r temp x)))
(define-vop (fast-*/signed=>signed fast-signed-binop)
(:translate *)
(:generator 3
(inst mul r x y)))
(define-vop (fast-*/unsigned=>unsigned fast-unsigned-binop)
(:translate *)
(:generator 3
(inst mul r x y)))
;;;
(define-vop (fast-lognor/fixnum=>fixnum fast-fixnum-binop)
(:translate lognor)
(:args (x :target r :scs (any-reg))
(y :target r :scs (any-reg)))
(:temporary (:sc non-descriptor-reg) temp)
(:generator 3
(inst orr temp x y)
(inst mvn temp temp)
(inst eor r temp fixnum-tag-mask)))
(define-vop (fast-logand/signed-unsigned=>unsigned fast-logand/unsigned=>unsigned)
(:args (x :scs (signed-reg) :target r)
(y :scs (unsigned-reg) :target r))
(:arg-types signed-num unsigned-num)
(:translate logand))
(define-source-transform logeqv (&rest args)
(if (oddp (length args))
`(logxor ,@args)
`(lognot (logxor ,@args))))
(define-source-transform logorc1 (x y)
`(logior (lognot ,x) ,y))
(define-source-transform logorc2 (x y)
`(logior ,x (lognot ,y)))
;;; Shifting
(define-vop (fast-ash-left-c/fixnum=>fixnum)
(:translate ash)
(:policy :fast-safe)
(:args (number :scs (any-reg) :target result))
(:info amount)
(:arg-types tagged-num (:constant unsigned-byte))
(:results (result :scs (any-reg)))
(:result-types tagged-num)
(:note "inline ASH")
(:generator 1
(if (< amount 32)
(inst mov result (lsl number amount))
(inst mov result 0))))
(define-vop (fast-ash-right-c/fixnum=>fixnum)
(:translate ash)
(:policy :fast-safe)
(:args (number :scs (any-reg) :target result))
(:info amount)
(:arg-types tagged-num (:constant (integer * -1)))
(:results (result :scs (any-reg)))
(:result-types tagged-num)
(:temporary (:sc unsigned-reg :target result) temp)
(:note "inline ASH")
(:generator 1
(inst mov temp (asr number (min (- amount) 31)))
(inst bic result temp fixnum-tag-mask)))
(define-vop (fast-ash-left-modfx-c/fixnum=>fixnum
fast-ash-left-c/fixnum=>fixnum)
(:translate ash-left-modfx))
(define-vop (fast-ash-left-mod32-c/fixnum=>fixnum
fast-ash-left-c/fixnum=>fixnum)
(:translate ash-left-mod32))
(define-vop (fast-ash-c/unsigned=>unsigned)
(:translate ash)
(:policy :fast-safe)
(:args (number :scs (unsigned-reg) :target result))
(:info amount)
(:arg-types unsigned-num (:constant integer))
(:results (result :scs (unsigned-reg)))
(:result-types unsigned-num)
(:note "inline ASH")
(:generator 3
(cond ((< -32 amount 32)
(if (plusp amount)
(inst mov result (lsl number amount))
(inst mov result (lsr number (- amount)))))
(t
(inst mov result 0)))))
(define-vop (fast-ash-c/signed=>signed)
(:translate ash)
(:policy :fast-safe)
(:args (number :scs (signed-reg) :target result))
(:info amount)
(:arg-types signed-num (:constant integer))
(:results (result :scs (signed-reg)))
(:result-types signed-num)
(:note "inline ASH")
(:generator 3
(cond ((< -32 amount 32)
(if (plusp amount)
(inst mov result (lsl number amount))
(inst mov result (asr number (- amount)))))
(t
(inst mov result 0)))))
(define-vop (fast-ash-left-mod32-c/unsigned=>unsigned
fast-ash-c/unsigned=>unsigned)
(:translate ash-left-mod32))
(define-vop (fast-ash-left-mod32-c/signed=>signed
fast-ash-c/signed=>signed)
(:translate ash-left-mod32))
(define-vop (fast-ash/signed/unsigned)
(:note "inline ASH")
(:args (number)
(amount))
(:results (result))
(:policy :fast-safe)
(:temporary (:sc non-descriptor-reg) temp)
(:variant-vars variant)
(:generator 5
(move temp amount)
(inst cmp temp 0)
(inst b :ge LEFT)
(inst rsb temp temp 0) ;; negate
(inst cmp temp sb!vm:n-word-bits)
(inst mov :gt temp sb!vm:n-word-bits)
(inst mov result (ecase variant
(:signed (asr number temp))
(:unsigned (lsr number temp))))
(inst b END)
LEFT
(inst cmp temp sb!vm:n-word-bits)
(inst mov :gt temp sb!vm:n-word-bits)
(inst mov result (lsl number temp))
END))
(define-vop (fast-ash/signed=>signed fast-ash/signed/unsigned)
(:args (number :scs (signed-reg) :to :save)
(amount :scs (signed-reg) :to :save :target temp))
(:arg-types signed-num signed-num)
(:results (result :scs (signed-reg)))
(:result-types signed-num)
(:translate ash)
(:variant :signed))
(define-vop (fast-ash/unsigned=>unsigned fast-ash/signed/unsigned)
(:args (number :scs (unsigned-reg) :to :save)
(amount :scs (signed-reg) :to :save))
(:arg-types unsigned-num signed-num)
(:results (result :scs (unsigned-reg)))
(:result-types unsigned-num)
(:translate ash)
(:variant :unsigned))
(macrolet ((def (name sc-type type result-type cost)
`(define-vop (,name)
(:note "inline ASH")
(:translate ash)
(:args (number :scs (,sc-type))
(amount :scs (signed-reg unsigned-reg)
:target temp))
(:temporary (:sc non-descriptor-reg) temp)
(:arg-types ,type positive-fixnum)
(:results (result :scs (,result-type)))
(:result-types ,type)
(:policy :fast-safe)
(:generator ,cost
(move temp amount)
(inst cmp temp sb!vm:n-word-bits)
(inst mov :gt temp sb!vm:n-word-bits)
(inst mov result (lsl number temp))))))
;; FIXME: There's the opportunity for a sneaky optimization here, I
;; think: a FAST-ASH-LEFT-C/FIXNUM=>SIGNED vop. -- CSR, 2003-09-03
(def fast-ash-left/fixnum=>fixnum any-reg tagged-num any-reg 2)
(def fast-ash-left/signed=>signed signed-reg signed-num signed-reg 3)
(def fast-ash-left/unsigned=>unsigned unsigned-reg unsigned-num unsigned-reg 3))
(define-vop (fast-ash-left-mod32/unsigned=>unsigned
fast-ash-left/unsigned=>unsigned)
(:translate ash-left-mod32))
#!+ash-right-vops
(define-vop (fast-%ash/right/unsigned)
(:translate %ash/right)
(:policy :fast-safe)
(:args (number :scs (unsigned-reg) :target result)
(amount :scs (unsigned-reg)))
(:arg-types unsigned-num unsigned-num)
(:results (result :scs (unsigned-reg) :from (:argument 0)))
(:result-types unsigned-num)
(:generator 4
(inst mov result (lsr number amount))))
#!+ash-right-vops
(define-vop (fast-%ash/right/signed)
(:translate %ash/right)
(:policy :fast-safe)
(:args (number :scs (signed-reg) :target result)
(amount :scs (unsigned-reg)))
(:arg-types signed-num unsigned-num)
(:results (result :scs (signed-reg) :from (:argument 0)))
(:result-types signed-num)
(:generator 4
(inst mov result (asr number amount))))
#!+ash-right-vops
(define-vop (fast-%ash/right/fixnum)
(:translate %ash/right)
(:policy :fast-safe)
(:args (number :scs (any-reg) :target result)
(amount :scs (unsigned-reg) :target temp))
(:arg-types tagged-num unsigned-num)
(:results (result :scs (any-reg) :from (:argument 0)))
(:result-types tagged-num)
(:temporary (:sc unsigned-reg :target result) temp)
(:generator 3
(inst mov temp (asr number amount))
(inst bic result temp fixnum-tag-mask)))
;;; Only the lower 5 bits of the shift amount are significant.
(define-vop (shift-towards-someplace)
(:policy :fast-safe)
(:args (num :scs (unsigned-reg))
(amount :scs (signed-reg)))
(:arg-types unsigned-num tagged-num)
(:temporary (:sc signed-reg) temp)
(:results (r :scs (unsigned-reg)))
(:result-types unsigned-num))
(define-vop (shift-towards-start shift-towards-someplace)
(:translate shift-towards-start)
(:note "SHIFT-TOWARDS-START")
(:generator 1
(inst and temp amount #b11111)
(inst mov r (lsr num temp))))
(define-vop (shift-towards-end shift-towards-someplace)
(:translate shift-towards-end)
(:note "SHIFT-TOWARDS-END")
(:generator 1
(inst and temp amount #b11111)
(inst mov r (lsl num temp))))
(define-vop (signed-byte-32-len)
(:translate integer-length)
(:note "inline (signed-byte 32) integer-length")
(:policy :fast-safe)
(:args (arg :scs (signed-reg) :target temp))
(:arg-types signed-num)
(:results (res :scs (any-reg)))
(:result-types positive-fixnum)
(:temporary (:scs (non-descriptor-reg) :from (:argument 0)) temp)
(:generator 30
(move temp arg)
(inst cmp temp 0)
(inst mvn :lt temp temp)
(inst clz temp temp)
(inst rsb temp temp 32)
(inst mov res (lsl temp n-fixnum-tag-bits))))
(define-vop (unsigned-byte-32-count)
(:translate logcount)
(:note "inline (unsigned-byte 32) logcount")
(:policy :fast-safe)
(:args (arg :scs (unsigned-reg) :target num))
(:arg-types unsigned-num)
(:results (res :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:temporary (:scs (non-descriptor-reg) :from (:argument 0) :to (:result 0)
:target res) num)
(:temporary (:scs (non-descriptor-reg)) mask temp)
(:generator 30
(move num arg)
(load-immediate-word mask #x55555555)
(inst and temp mask (lsr num 1))
(inst and num num mask)
(inst add num num temp)
(load-immediate-word mask #x33333333)
(inst and temp mask (lsr num 2))
(inst and num num mask)
(inst add num num temp)
(load-immediate-word mask #x0f0f0f0f)
(inst and temp mask (lsr num 4))
(inst and num num mask)
(inst add num num temp)
(inst add num num (lsr num 8))
(inst add num num (lsr num 16))
(inst and res num #xff)))
;;; Modular functions
(define-modular-fun lognot-mod32 (x) lognot :untagged nil 32)
(define-vop (lognot-mod32/unsigned=>unsigned)
(:translate lognot-mod32)
(:args (x :scs (unsigned-reg)))
(:arg-types unsigned-num)
(:results (res :scs (unsigned-reg)))
(:result-types unsigned-num)
(:policy :fast-safe)
(:generator 1
(inst mvn res x)))
(macrolet
((define-modular-backend (fun &optional constantp)
(let ((mfun-name (symbolicate fun '-mod32))
(modvop (symbolicate 'fast- fun '-mod32/unsigned=>unsigned))
(modcvop (symbolicate 'fast- fun 'mod32-c/unsigned=>unsigned))
(vop (symbolicate 'fast- fun '/unsigned=>unsigned))
(cvop (symbolicate 'fast- fun '-c/unsigned=>unsigned)))
`(progn
(define-modular-fun ,mfun-name (x y) ,fun :untagged nil 32)
(define-vop (,modvop ,vop)
(:translate ,mfun-name))
,@(when constantp
`((define-vop (,modcvop ,cvop)
(:translate ,mfun-name))))))))
(define-modular-backend + t)
(define-modular-backend - t)
(define-modular-backend *)
;; (define-modular-backend logeqv)
;; (define-modular-backend lognand)
;; (define-modular-backend lognor)
(define-modular-backend logandc1)
(define-modular-backend logandc2)
;; (define-modular-backend logorc1)
;; (define-modular-backend logorc2)
)
;;;; Binary conditional VOPs:
(define-vop (fast-conditional)
(:conditional :eq)
(:effects)
(:affected)
(:policy :fast-safe))
(define-vop (fast-conditional/fixnum fast-conditional)
(:args (x :scs (any-reg))
(y :scs (any-reg)))
(:arg-types tagged-num tagged-num)
(:note "inline fixnum comparison"))
(define-vop (fast-conditional-c/fixnum fast-conditional/fixnum)
(:args (x :scs (any-reg)))
(:arg-types tagged-num (:constant (unsigned-byte 8)))
(:info y))
(define-vop (fast-conditional/signed fast-conditional)
(:args (x :scs (signed-reg))
(y :scs (signed-reg)))
(:arg-types signed-num signed-num)
(:note "inline (signed-byte 32) comparison"))
(define-vop (fast-conditional-c/signed fast-conditional/signed)
(:args (x :scs (signed-reg)))
(:arg-types signed-num (:constant (unsigned-byte 8)))
(:info y))
(define-vop (fast-conditional/unsigned fast-conditional)
(:args (x :scs (unsigned-reg))
(y :scs (unsigned-reg)))
(:arg-types unsigned-num unsigned-num)
(:note "inline (unsigned-byte 32) comparison"))
(define-vop (fast-conditional-c/unsigned fast-conditional/unsigned)
(:args (x :scs (unsigned-reg)))
(:arg-types unsigned-num (:constant (unsigned-byte 8)))
(:info y))
(defmacro define-conditional-vop (tran cond unsigned)
`(progn
,@(mapcar (lambda (suffix cost signed)
(unless (and (member suffix '(/fixnum -c/fixnum))
(eq tran 'eql))
`(define-vop (,(intern (format nil "~:@(FAST-IF-~A~A~)"
tran suffix))
,(intern
(format nil "~:@(FAST-CONDITIONAL~A~)"
suffix)))
(:translate ,tran)
(:conditional ,(if signed cond unsigned))
(:generator ,cost
(inst cmp x
,(if (eq suffix '-c/fixnum) '(fixnumize y) 'y))))))
'(/fixnum -c/fixnum /signed -c/signed /unsigned -c/unsigned)
'(4 3 6 5 6 5)
'(t t t t nil nil))))
(define-conditional-vop < :lt :lo)
(define-conditional-vop > :gt :hi)
(define-conditional-vop eql :eq :eq)
;;; EQL/FIXNUM is funny because the first arg can be of any type, not
;;; just a known fixnum.
;;; These versions specify a fixnum restriction on their first arg.
;;; We have also generic-eql/fixnum VOPs which are the same, but have
;;; no restriction on the first arg and a higher cost. The reason for
;;; doing this is to prevent fixnum specific operations from being
;;; used on word integers, spuriously consing the argument.
(define-vop (fast-eql/fixnum)
(:args (x :scs (any-reg))
(y :scs (any-reg)))
(:arg-types tagged-num tagged-num)
(:note "inline fixnum comparison")
(:translate eql)
(:conditional :eq)
(:policy :fast-safe)
(:generator 4
(inst cmp x y)))
(define-vop (generic-eql/fixnum fast-eql/fixnum)
(:args (x :scs (any-reg descriptor-reg))
(y :scs (any-reg)))
(:arg-types * tagged-num)
(:variant-cost 7))
(define-vop (fast-eql-c/fixnum)
(:args (x :scs (any-reg)))
(:arg-types tagged-num (:constant (signed-byte 9)))
(:info y)
(:translate eql)
(:policy :fast-safe)
(:conditional :eq)
(:generator 3
(if (minusp y)
(inst cmn x (fixnumize (abs y)))
(inst cmp x (fixnumize y)))))
(define-vop (generic-eql-c/fixnum fast-eql-c/fixnum)
(:args (x :scs (any-reg descriptor-reg)))
(:arg-types * (:constant (signed-byte 9)))
(:variant-cost 6))
(macrolet ((define-logtest-vops ()
`(progn
,@(loop for suffix in '(/fixnum -c/fixnum
/signed -c/signed
/unsigned -c/unsigned)
for cost in '(4 3 6 5 6 5)
collect
`(define-vop (,(symbolicate "FAST-LOGTEST" suffix)
,(symbolicate "FAST-CONDITIONAL" suffix))
(:translate logtest)
(:conditional :ne)
(:generator ,cost
(inst tst x
,(case suffix
(-c/fixnum
`(fixnumize y))
((-c/signed -c/unsigned)
`y)
(t
'y)))))))))
(define-logtest-vops))
(define-source-transform lognand (x y)
`(lognot (logand ,x ,y)))
(defknown %logbitp (integer unsigned-byte) boolean
(movable foldable flushable always-translatable))
;;; For constant folding
(defun %logbitp (integer index)
(logbitp index integer))
(define-vop (fast-logbitp-c/fixnum fast-conditional-c/fixnum)
(:translate %logbitp)
(:conditional :ne)
(:arg-types tagged-num (:constant (integer 0 29)))
(:generator 4
(inst tst x (ash 1 (+ y n-fixnum-tag-bits)))))
(define-vop (fast-logbitp-c/signed fast-conditional-c/signed)
(:translate %logbitp)
(:conditional :ne)
(:arg-types signed-num (:constant (integer 0 31)))
(:generator 5
(inst tst x (ash 1 y))))
(define-vop (fast-logbitp-c/unsigned fast-conditional-c/unsigned)
(:translate %logbitp)
(:conditional :ne)
(:arg-types unsigned-num (:constant (integer 0 31)))
(:generator 5
(inst tst x (ash 1 y))))
(define-vop (fast-signum-fixnum fixnum-unop)
(:args (x :scs (any-reg) :target res))
(:translate signum)
(:generator 4
(move res x)
(inst cmp x 0)
(inst mov :ne res (fixnumize 1))
(inst mvn :mi res (lognot (fixnumize -1)))))
(define-vop (fast-signum-signed signed-unop)
(:args (x :scs (signed-reg) :target res))
(:translate signum)
(:generator 5
(move res x)
(inst cmp x 0)
(inst mov :ne res 1)
(inst mvn :mi res 0)))
(define-vop (fast-signum-unsigned unsigned-unop)
(:args (x :scs (unsigned-reg) :target res))
(:translate signum)
(:generator 5
(move res x)
(inst cmp x 0)
(inst mov :ne res 1)))
;; Specialised mask-signed-field VOPs.
(define-vop (mask-signed-field-word/c)
(:translate sb!c::mask-signed-field)
(:policy :fast-safe)
(:args (x :scs (signed-reg unsigned-reg) :target r))
(:arg-types (:constant (integer 0 32)) untagged-num)
(:results (r :scs (signed-reg)))
(:result-types signed-num)
(:info width)
(:generator 3
(cond ((zerop width)
(inst mov r 0))
((= width 32)
(move r x))
(t
(let ((delta (- n-word-bits width)))
(inst mov r (lsl x delta))
(inst mov r (asr r delta)))))))
(define-vop (mask-signed-field-bignum/c)
(:translate sb!c::mask-signed-field)
(:policy :fast-safe)
(:args (x :scs (descriptor-reg) :target r))
(:arg-types (:constant (integer 0 32)) bignum)
(:results (r :scs (signed-reg)))
(:result-types signed-num)
(:info width)
(:generator 4
(cond ((zerop width)
(inst mov r 0))
(t
(loadw r x bignum-digits-offset other-pointer-lowtag)
(let ((delta (- n-word-bits width)))
(inst mov r (lsl r delta))
(inst mov r (asr r delta)))))))
;;;; Bignum stuff.
(define-vop (bignum-length get-header-data)
(:translate sb!bignum:%bignum-length)
(:policy :fast-safe))
(define-vop (bignum-set-length set-header-data)
(:translate sb!bignum:%bignum-set-length)
(:policy :fast-safe))
(define-full-reffer bignum-ref * bignum-digits-offset other-pointer-lowtag
(unsigned-reg) unsigned-num sb!bignum:%bignum-ref)
(define-full-setter bignum-set * bignum-digits-offset other-pointer-lowtag
(unsigned-reg) unsigned-num sb!bignum:%bignum-set)
(define-vop (digit-0-or-plus)
(:translate sb!bignum:%digit-0-or-plusp)
(:policy :fast-safe)
(:args (digit :scs (unsigned-reg)))
(:arg-types unsigned-num)
(:conditional)
(:info target not-p)
(:generator 2
(inst cmp digit 0)
(inst b (if not-p :lt :ge) target)))
(define-vop (add-w/carry)
(:translate sb!bignum:%add-with-carry)
(:policy :fast-safe)
(:args (a :scs (unsigned-reg))
(b :scs (unsigned-reg))
(c :scs (any-reg)))
(:arg-types unsigned-num unsigned-num positive-fixnum)
(:results (result :scs (unsigned-reg))
(carry :scs (unsigned-reg) :from :eval))
(:result-types unsigned-num positive-fixnum)
(:generator 3
(inst cmp c 1) ;; Set carry if (fixnum 0 or 1) c=0, else clear.
(inst adcs result a b)
(inst mov :cs carry 1)
(inst mov :cc carry 0)))
(define-vop (sub-w/borrow)
(:translate sb!bignum:%subtract-with-borrow)
(:policy :fast-safe)
(:args (a :scs (unsigned-reg))
(b :scs (unsigned-reg))
(c :scs (any-reg)))
(:arg-types unsigned-num unsigned-num positive-fixnum)
(:results (result :scs (unsigned-reg))
(borrow :scs (unsigned-reg) :from :eval))
(:result-types unsigned-num positive-fixnum)
(:generator 4
(inst cmp c 1) ;; Set carry if (fixnum 0 or 1) c=0, else clear.
(inst sbcs result a b)
(inst mov :cs borrow 1)
(inst mov :cc borrow 0)))
(define-vop (bignum-mult-and-add-3-arg)
(:translate sb!bignum:%multiply-and-add)
(:policy :fast-safe)
(:args (x :scs (unsigned-reg) :to :result)
(y :scs (unsigned-reg) :to :result)
(carry-in :scs (unsigned-reg) :target lo))
(:arg-types unsigned-num unsigned-num unsigned-num)
(:results (hi :scs (unsigned-reg) :from :eval)
(lo :scs (unsigned-reg) :from (:argument 2)))
(:result-types unsigned-num unsigned-num)
(:generator 2
(move lo carry-in)
(inst mov hi 0)
(inst umlal lo hi x y)))
(define-vop (bignum-mult-and-add-4-arg)
(:translate sb!bignum:%multiply-and-add)
(:policy :fast-safe)
(:args (x :scs (unsigned-reg) :to :result)
(y :scs (unsigned-reg) :to :result)
(prev :scs (unsigned-reg) :to :eval)
(carry-in :scs (unsigned-reg) :to :eval))
(:arg-types unsigned-num unsigned-num unsigned-num unsigned-num)
(:results (hi :scs (unsigned-reg) :from :eval)
(lo :scs (unsigned-reg) :from :eval))
(:result-types unsigned-num unsigned-num)
(:generator 9
(inst adds lo prev carry-in)
(inst mov :cs hi 1)
(inst mov :cc hi 0)
(inst umlal lo hi x y)))
(define-vop (bignum-mult)
(:translate sb!bignum:%multiply)
(:policy :fast-safe)
(:args (x :scs (unsigned-reg))
(y :scs (unsigned-reg)))
(:arg-types unsigned-num unsigned-num)
(:results (hi :scs (unsigned-reg))
(lo :scs (unsigned-reg)))
(:result-types unsigned-num unsigned-num)
(:generator 1
(inst umull lo hi x y)))
#!+multiply-high-vops
(define-vop (mulhi)
(:translate %multiply-high)
(:policy :fast-safe)
(:args (x :scs (unsigned-reg) :target hi)
(y :scs (unsigned-reg)))
(:arg-types unsigned-num unsigned-num)
(:temporary (:sc unsigned-reg) lo)
(:results (hi :scs (unsigned-reg)))
(:result-types unsigned-num)
(:generator 20
(inst umull lo hi x y)))
#!+multiply-high-vops
(define-vop (mulhi/fx)
(:translate %multiply-high)
(:policy :fast-safe)
(:args (x :scs (any-reg) :target hi)
(y :scs (unsigned-reg)))
(:arg-types positive-fixnum unsigned-num)
(:temporary (:sc any-reg) lo)
(:results (hi :scs (any-reg)))
(:result-types positive-fixnum)
(:generator 15
(inst umull lo hi x y)
(inst bic hi hi fixnum-tag-mask)))
(define-vop (bignum-lognot lognot-mod32/unsigned=>unsigned)
(:translate sb!bignum:%lognot))
(define-vop (bignum-floor)
(:translate sb!bignum:%bigfloor)
(:policy :fast-safe)
(:args (div-high :scs (unsigned-reg) :target rem)
(div-low :scs (unsigned-reg) :target quo)
(divisor :scs (unsigned-reg)))
(:arg-types unsigned-num unsigned-num unsigned-num)
(:results (quo :scs (unsigned-reg) :from (:argument 1))
(rem :scs (unsigned-reg) :from (:argument 0)))
(:result-types unsigned-num unsigned-num)
(:generator 300
(move rem div-high)
(move quo div-low)
(dotimes (i 33)
(inst cmp rem divisor)
(inst sub :hs rem rem divisor)
(inst adcs quo quo quo)
(unless (= i 32)
(inst adc rem rem rem)))))
(define-vop (signify-digit)
(:translate sb!bignum:%fixnum-digit-with-correct-sign)
(:policy :fast-safe)
(:args (digit :scs (unsigned-reg) :target res))
(:arg-types unsigned-num)
(:results (res :scs (any-reg signed-reg)))
(:result-types signed-num)
(:generator 1
(if (sc-is res any-reg)
(inst mov res (lsl digit n-fixnum-tag-bits))
(inst mov res digit))))
(define-vop (digit-ashr)
(:translate sb!bignum:%ashr)
(:policy :fast-safe)
(:args (digit :scs (unsigned-reg))
(count :scs (unsigned-reg)))
(:arg-types unsigned-num positive-fixnum)
(:results (result :scs (unsigned-reg)))
(:result-types unsigned-num)
(:generator 1
(inst mov result (asr digit count))))
(define-vop (digit-lshr digit-ashr)
(:translate sb!bignum:%digit-logical-shift-right)
(:generator 1
(inst mov result (lsr digit count))))
(define-vop (digit-ashl digit-ashr)
(:translate sb!bignum:%ashl)
(:generator 1
(inst mov result (lsl digit count))))
;;;; Static functions.
(define-static-fun two-arg-gcd (x y) :translate gcd)
(define-static-fun two-arg-lcm (x y) :translate lcm)
(define-static-fun two-arg-+ (x y) :translate +)
(define-static-fun two-arg-- (x y) :translate -)
(define-static-fun two-arg-* (x y) :translate *)
(define-static-fun two-arg-/ (x y) :translate /)
(define-static-fun two-arg-< (x y) :translate <)
(define-static-fun two-arg-> (x y) :translate >)
(define-static-fun two-arg-= (x y) :translate =)
(define-static-fun two-arg-and (x y) :translate logand)
(define-static-fun two-arg-ior (x y) :translate logior)
(define-static-fun two-arg-xor (x y) :translate logxor)
(define-static-fun two-arg-eqv (x y) :translate logeqv)
(define-static-fun eql (x y) :translate eql)
(define-static-fun %negate (x) :translate %negate)

View file

@ -0,0 +1,426 @@
;;;; array operations for the ARM VM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; Allocator for the array header.
(define-vop (make-array-header)
(:translate make-array-header)
(:policy :fast-safe)
(:args (type :scs (any-reg))
(rank :scs (any-reg)))
(:arg-types tagged-num tagged-num)
(:temporary (:scs (descriptor-reg) :to (:result 0) :target result) header)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:temporary (:scs (non-descriptor-reg)) ndescr)
(:results (result :scs (descriptor-reg)))
(:generator 5
;; Compute the allocation size.
(inst add ndescr rank (+ (* (1+ array-dimensions-offset) n-word-bytes)
lowtag-mask))
(inst bic ndescr ndescr lowtag-mask)
(pseudo-atomic (pa-flag)
(allocation header ndescr other-pointer-lowtag :flag-tn pa-flag)
;; Now that we have the space allocated, compute the header
;; value.
(inst add ndescr rank (fixnumize (1- array-dimensions-offset)))
(inst mov ndescr (lsl ndescr (- n-widetag-bits n-fixnum-tag-bits)))
(inst orr ndescr ndescr (lsr type n-fixnum-tag-bits))
;; And store the header value.
(storew ndescr header 0 other-pointer-lowtag))
(move result header)))
(define-vop (make-array-header/c)
(:translate make-array-header)
(:policy :fast-safe)
(:arg-types (:constant t) (:constant t))
(:info type rank)
(:temporary (:scs (descriptor-reg) :to (:result 0) :target result) header)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:results (result :scs (descriptor-reg)))
(:generator 4
(let* ((header-size (+ rank
(1- array-dimensions-offset)))
(bytes (logandc2 (+ (* (1+ header-size) n-word-bytes)
lowtag-mask)
lowtag-mask))
(header-bits (logior (ash header-size
n-widetag-bits)
type)))
(pseudo-atomic (pa-flag)
(allocation header bytes other-pointer-lowtag :flag-tn pa-flag)
(load-immediate-word pa-flag header-bits)
(storew pa-flag header 0 other-pointer-lowtag)))
(move result header)))
;;;; Additional accessors and setters for the array header.
(define-full-reffer %array-dimension *
array-dimensions-offset other-pointer-lowtag
(any-reg) positive-fixnum sb!kernel:%array-dimension)
(define-full-setter %set-array-dimension *
array-dimensions-offset other-pointer-lowtag
(any-reg) positive-fixnum sb!kernel:%set-array-dimension)
(define-vop (array-rank-vop)
(:translate sb!kernel:%array-rank)
(:policy :fast-safe)
(:args (x :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) temp)
(:results (res :scs (any-reg descriptor-reg)))
(:generator 6
(loadw temp x 0 other-pointer-lowtag)
(inst mov temp (asr temp n-widetag-bits))
(inst sub temp temp (1- array-dimensions-offset))
(inst mov res (lsl temp n-fixnum-tag-bits))))
;;;; Bounds checking routine.
(define-vop (check-bound)
(:translate %check-bound)
(:policy :fast-safe)
(:args (array :scs (descriptor-reg))
(bound :scs (any-reg descriptor-reg))
(index :scs (any-reg descriptor-reg) :target result))
(:results (result :scs (any-reg descriptor-reg)))
(:vop-var vop)
(:save-p :compute-only)
(:generator 5
(let ((error (generate-error-code vop 'invalid-array-index-error array bound index)))
(inst cmp index bound)
(inst b :hs error)
(move result index))))
;;;; Accessors/Setters
;;; Variants built on top of word-index-ref, etc. I.e. those vectors whos
;;; elements are represented in integer registers and are built out of
;;; 8, 16, or 32 bit elements.
(macrolet ((def-full-data-vector-frobs (type element-type &rest scs)
`(progn
(define-full-reffer ,(symbolicate "DATA-VECTOR-REF/" type) ,type
vector-data-offset other-pointer-lowtag
,(remove-if #'(lambda (x) (member x '(null))) scs)
,element-type
data-vector-ref)
(define-full-setter ,(symbolicate "DATA-VECTOR-SET/" type) ,type
vector-data-offset other-pointer-lowtag ,scs ,element-type
data-vector-set)))
(def-partial-data-vector-frobs (type element-type size signed &rest scs)
`(progn
(define-partial-reffer ,(symbolicate "DATA-VECTOR-REF/" type) ,type
,size ,signed vector-data-offset other-pointer-lowtag ,scs
,element-type data-vector-ref)
(define-partial-setter ,(symbolicate "DATA-VECTOR-SET/" type) ,type
,size vector-data-offset other-pointer-lowtag ,scs
,element-type data-vector-set))))
(def-full-data-vector-frobs simple-vector *
descriptor-reg any-reg null)
(def-partial-data-vector-frobs simple-base-string character
:byte nil character-reg)
#!+sb-unicode
(def-full-data-vector-frobs simple-character-string character character-reg)
(def-partial-data-vector-frobs simple-array-unsigned-byte-7 positive-fixnum
:byte nil unsigned-reg signed-reg)
(def-partial-data-vector-frobs simple-array-unsigned-byte-8 positive-fixnum
:byte nil unsigned-reg signed-reg)
(def-partial-data-vector-frobs simple-array-unsigned-byte-15 positive-fixnum
:short nil unsigned-reg signed-reg)
(def-partial-data-vector-frobs simple-array-unsigned-byte-16 positive-fixnum
:short nil unsigned-reg signed-reg)
(def-full-data-vector-frobs simple-array-unsigned-byte-31 unsigned-num
unsigned-reg)
(def-full-data-vector-frobs simple-array-unsigned-byte-32 unsigned-num
unsigned-reg)
(def-partial-data-vector-frobs simple-array-signed-byte-8 tagged-num
:byte t signed-reg)
(def-partial-data-vector-frobs simple-array-signed-byte-16 tagged-num
:short t signed-reg)
(def-full-data-vector-frobs simple-array-unsigned-fixnum positive-fixnum
any-reg)
(def-full-data-vector-frobs simple-array-fixnum tagged-num
any-reg)
(def-full-data-vector-frobs simple-array-signed-byte-32 signed-num
signed-reg))
;;; Integer vectors whose elements are smaller than a byte. I.e. bit, 2-bit,
;;; and 4-bit vectors.
(macrolet ((def-small-data-vector-frobs (type bits)
(let* ((elements-per-word (floor n-word-bits bits))
(bit-shift (1- (integer-length elements-per-word))))
`(progn
(define-vop (,(symbolicate "DATA-VECTOR-REF/" type))
(:note "inline array access")
(:translate data-vector-ref)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (unsigned-reg)))
(:arg-types ,type positive-fixnum)
(:results (value :scs (any-reg)))
(:result-types positive-fixnum)
(:temporary (:scs (interior-reg)) lip)
(:temporary (:scs (non-descriptor-reg) :to (:result 0)) temp result)
(:generator 20
;; Compute the offset for the word we're interested in.
(inst mov temp (lsr index ,bit-shift))
;; Load the word in question.
(inst add lip object (lsl temp word-shift))
(inst ldr result (@ lip
(- (* vector-data-offset n-word-bytes)
other-pointer-lowtag)))
;; Compute the position of the bitfield we need.
(inst and temp index ,(1- elements-per-word))
,@(when (eq *backend-byte-order* :big-endian)
`((inst eor temp temp ,(1- elements-per-word))))
,@(unless (= bits 1)
`((inst mov temp (lsl temp ,(1- (integer-length bits))))))
;; Shift the field we need to the low bits of RESULT.
(inst mov result (lsr result temp))
;; Mask out the field we're interested in.
(inst and result result ,(1- (ash 1 bits)))
;; And fixnum-tag the result.
(inst mov value (lsl result n-fixnum-tag-bits))))
(define-vop (,(symbolicate "DATA-VECTOR-SET/" type))
(:note "inline array store")
(:translate data-vector-set)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (unsigned-reg) :target shift)
(value :scs (unsigned-reg immediate) :target result))
(:arg-types ,type positive-fixnum positive-fixnum)
(:results (result :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:temporary (:scs (interior-reg)) lip)
(:temporary (:scs (non-descriptor-reg)) temp old)
(:temporary (:scs (non-descriptor-reg) :from (:argument 1)) shift)
(:generator 25
;; Compute the offset for the word we're interested in.
(inst mov temp (lsr index ,bit-shift))
(inst mov temp (lsl temp n-fixnum-tag-bits))
;; Load the word in question.
(inst add lip object temp)
(inst ldr old (@ lip
(- (* vector-data-offset n-word-bytes)
other-pointer-lowtag)))
;; Compute the position of the bitfield we need.
(inst and shift index ,(1- elements-per-word))
,@(when (eq *backend-byte-order* :big-endian)
`((inst eor shift ,(1- elements-per-word))))
,@(unless (= bits 1)
`((inst mov shift (lsl shift ,(1- (integer-length bits))))))
;; Clear the target bitfield.
(unless (and (sc-is value immediate)
(= (tn-value value) ,(1- (ash 1 bits))))
(inst mov temp ,(1- (ash 1 bits)))
(inst bic old old (lsl temp shift)))
;; LOGIOR in the new value (shifted appropriatly).
(sc-case value
(immediate
(inst mov temp (logand (tn-value value) ,(1- (ash 1 bits)))))
(unsigned-reg
(inst and temp value ,(1- (ash 1 bits)))))
(inst orr old old (lsl temp shift))
;; Write the altered word back to the array.
(inst str old (@ lip
(- (* vector-data-offset n-word-bytes)
other-pointer-lowtag)))
;; And present the result properly.
(sc-case value
(immediate
(inst mov result (tn-value value)))
(unsigned-reg
(move result value)))))))))
(def-small-data-vector-frobs simple-bit-vector 1)
(def-small-data-vector-frobs simple-array-unsigned-byte-2 2)
(def-small-data-vector-frobs simple-array-unsigned-byte-4 4))
;;; And the float variants.
(define-vop (data-vector-ref/simple-array-single-float)
(:note "inline array access")
(:translate data-vector-ref)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg)))
(:arg-types simple-array-single-float positive-fixnum)
(:results (value :scs (single-reg)))
(:temporary (:scs (interior-reg)) lip)
(:result-types single-float)
(:generator 5
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip index)
(inst flds value (@ lip))))
(define-vop (data-vector-set/simple-array-single-float)
(:note "inline array store")
(:translate data-vector-set)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg))
(value :scs (single-reg) :target result))
(:arg-types simple-array-single-float positive-fixnum single-float)
(:results (result :scs (single-reg)))
(:result-types single-float)
(:temporary (:scs (interior-reg)) lip)
(:generator 5
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip index)
(inst fsts value (@ lip))
(unless (location= result value)
(inst fcpys result value))))
(define-vop (data-vector-ref/simple-array-double-float)
(:note "inline array access")
(:translate data-vector-ref)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg)))
(:arg-types simple-array-double-float positive-fixnum)
(:results (value :scs (double-reg)))
(:result-types double-float)
(:temporary (:scs (interior-reg)) lip)
(:generator 7
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip (lsl index 1))
(inst fldd value (@ lip))))
(define-vop (data-vector-set/simple-array-double-float)
(:note "inline array store")
(:translate data-vector-set)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg))
(value :scs (double-reg) :target result))
(:arg-types simple-array-double-float positive-fixnum double-float)
(:results (result :scs (double-reg)))
(:result-types double-float)
(:temporary (:scs (interior-reg)) lip)
(:generator 20
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip (lsl index 1))
(inst fstd value (@ lip))
(unless (location= result value)
(inst fcpyd result value))))
;;; Complex float arrays.
(define-vop (data-vector-ref/simple-array-complex-single-float)
(:note "inline array access")
(:translate data-vector-ref)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg) :to :result)
(index :scs (any-reg)))
(:arg-types simple-array-complex-single-float positive-fixnum)
(:results (value :scs (complex-single-reg)))
(:temporary (:scs (interior-reg)) lip)
(:result-types complex-single-float)
(:generator 5
(let ((real-tn (complex-single-reg-real-tn value)))
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip (lsl index 1))
(inst flds real-tn (@ lip)))
(let ((imag-tn (complex-single-reg-imag-tn value)))
(inst flds imag-tn (@ lip n-word-bytes)))))
(define-vop (data-vector-set/simple-array-complex-single-float)
(:note "inline array store")
(:translate data-vector-set)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg) :to :result)
(index :scs (any-reg))
(value :scs (complex-single-reg) :target result))
(:arg-types simple-array-complex-single-float positive-fixnum
complex-single-float)
(:results (result :scs (complex-single-reg)))
(:result-types complex-single-float)
(:temporary (:scs (interior-reg)) lip)
(:generator 5
(let ((value-real (complex-single-reg-real-tn value))
(result-real (complex-single-reg-real-tn result)))
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip (lsl index 1))
(inst fsts value-real (@ lip))
(unless (location= result-real value-real)
(inst fcpys result-real value-real)))
(let ((value-imag (complex-single-reg-imag-tn value))
(result-imag (complex-single-reg-imag-tn result)))
(inst fsts value-imag (@ lip n-word-bytes))
(unless (location= result-imag value-imag)
(inst fcpys result-imag value-imag)))))
(define-vop (data-vector-ref/simple-array-complex-double-float)
(:note "inline array access")
(:translate data-vector-ref)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg) :to :result)
(index :scs (any-reg)))
(:arg-types simple-array-complex-double-float positive-fixnum)
(:results (value :scs (complex-double-reg)))
(:result-types complex-double-float)
(:temporary (:scs (interior-reg)) lip)
(:generator 7
(let ((real-tn (complex-double-reg-real-tn value)))
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip (lsl index 2))
(inst fldd real-tn (@ lip)))
(let ((imag-tn (complex-double-reg-imag-tn value)))
(inst fldd imag-tn (@ lip (* 2 n-word-bytes))))))
(define-vop (data-vector-set/simple-array-complex-double-float)
(:note "inline array store")
(:translate data-vector-set)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg) :to :result)
(index :scs (any-reg))
(value :scs (complex-double-reg) :target result))
(:arg-types simple-array-complex-double-float positive-fixnum
complex-double-float)
(:results (result :scs (complex-double-reg)))
(:result-types complex-double-float)
(:temporary (:scs (interior-reg)) lip)
(:generator 20
(let ((value-real (complex-double-reg-real-tn value))
(result-real (complex-double-reg-real-tn result)))
(inst add lip object (- (* vector-data-offset n-word-bytes)
other-pointer-lowtag))
(inst add lip lip (lsl index 2))
(inst fstd value-real (@ lip))
(unless (location= result-real value-real)
(inst fcpyd result-real value-real)))
(let ((value-imag (complex-double-reg-imag-tn value))
(result-imag (complex-double-reg-imag-tn result)))
(inst fstd value-imag (@ lip (* 2 n-word-bytes)))
(unless (location= result-imag value-imag)
(inst fcpyd result-imag value-imag)))))
;;; These vops are useful for accessing the bits of a vector irrespective of
;;; what type of vector it is.
(define-full-reffer vector-raw-bits * vector-data-offset other-pointer-lowtag
(unsigned-reg) unsigned-num %vector-raw-bits)
(define-full-setter set-vector-raw-bits * vector-data-offset other-pointer-lowtag
(unsigned-reg) unsigned-num %set-vector-raw-bits)

View file

@ -0,0 +1,23 @@
(in-package "SB!VM")
(def!constant +backend-fasl-file-implementation+ :arm)
(setf *backend-byte-order*
#!+little-endian :little-endian
#!-little-endian :big-endian)
(eval-when (:compile-toplevel :load-toplevel :execute)
;; Minumum observed value, not authoritative.
(setf *backend-page-bytes* 4096))
;;; The size in bytes of GENCGC cards, i.e. the granularity at which
;;; writes to old generations are logged. With mprotect-based write
;;; barriers, this must be a multiple of the OS page size.
(def!constant gencgc-card-bytes *backend-page-bytes*)
;;; The minimum size of new allocation regions. While it doesn't
;;; currently make a lot of sense to have a card size lower than
;;; the alloc granularity, it will, once we are smarter about finding
;;; the start of objects.
(def!constant gencgc-alloc-granularity 0)
;;; The minimum size at which we release address ranges to the OS.
;;; This must be a multiple of the OS page size.
(def!constant gencgc-release-granularity *backend-page-bytes*)

View file

@ -0,0 +1,549 @@
;;;; VOPs and other machine-specific support routines for call-out to C
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(defconstant +number-stack-alignment-mask+ 7)
(defconstant +max-register-args+ 4)
(defun my-make-wired-tn (prim-type-name sc-name offset)
(make-wired-tn (primitive-type-or-lose prim-type-name)
(sc-number-or-lose sc-name)
offset))
(defstruct arg-state
(num-register-args 0)
#!-arm-softfp
(fp-registers 0)
(stack-frame-size 0))
(defstruct (result-state (:copier nil))
(num-results 0))
(defun result-reg-offset (slot)
(ecase slot
(0 nargs-offset)
(1 nl3-offset)))
(defun register-args-offset (index)
(elt '(#.ocfp-offset #.nargs-offset #.nl2-offset #.nl3-offset)
index))
(defun int-arg (state prim-type reg-sc stack-sc)
(let ((reg-args (arg-state-num-register-args state)))
(cond ((< reg-args +max-register-args+)
(setf (arg-state-num-register-args state) (1+ reg-args))
(my-make-wired-tn prim-type reg-sc (register-args-offset reg-args)))
(t
(let ((frame-size (arg-state-stack-frame-size state)))
(setf (arg-state-stack-frame-size state) (1+ frame-size))
(my-make-wired-tn prim-type stack-sc frame-size))))))
(define-alien-type-method (integer :arg-tn) (type state)
(if (alien-integer-type-signed type)
(int-arg state 'signed-byte-32 'signed-reg 'signed-stack)
(int-arg state 'unsigned-byte-32 'unsigned-reg 'unsigned-stack)))
(define-alien-type-method (system-area-pointer :arg-tn) (type state)
(declare (ignore type))
(int-arg state 'system-area-pointer 'sap-reg 'sap-stack))
#!+arm-softfp
(define-alien-type-method (single-float :arg-tn) (type state)
(declare (ignore type))
(int-arg state 'single-float 'unsigned-reg 'single-stack))
#!-arm-softfp
(define-alien-type-method (single-float :arg-tn) (type state)
(declare (ignore type))
(let ((register (arg-state-fp-registers state)))
(cond ((> register 15)
(let ((frame-size (arg-state-stack-frame-size state)))
(setf (arg-state-stack-frame-size state) (1+ frame-size))
(my-make-wired-tn 'single-float 'single-stack frame-size)))
(t
(incf (arg-state-fp-registers state))
(my-make-wired-tn 'single-float 'single-reg register)))))
#!+arm-softfp
(define-alien-type-method (double-float :arg-tn) (type state)
(declare (ignore type))
(let* ((register (arg-state-num-register-args state))
;; The registers used are aligned, only r0-r1 and r2-r3 pairs
;; can be used.
(register (+ register (logand register 1))))
(cond ((> (+ register 2) +max-register-args+)
(setf (arg-state-num-register-args state) +max-register-args+)
(let ((frame-size (arg-state-stack-frame-size state)))
(setf (arg-state-stack-frame-size state) (+ frame-size 2))
(my-make-wired-tn 'double-float 'double-stack frame-size)))
(t
(setf (arg-state-num-register-args state) (+ register 2))
(list
(my-make-wired-tn 'unsigned-byte-32 'unsigned-reg
(register-args-offset register))
(my-make-wired-tn 'unsigned-byte-32 'unsigned-reg
(register-args-offset (1+ register)))
'move-double-to-int-args)))))
#!-arm-softfp
(define-alien-type-method (double-float :arg-tn) (type state)
(declare (ignore type))
(let ((register (setf (arg-state-fp-registers state)
(logandc2 (+ (arg-state-fp-registers state) 1) 1))))
(cond ((> register 15)
(let ((frame-size
;; align
(setf (arg-state-stack-frame-size state)
(logandc2 (+ (arg-state-stack-frame-size state) 1) 1))))
(setf (arg-state-stack-frame-size state) (+ frame-size 2))
(my-make-wired-tn 'double-float 'double-stack frame-size)))
(t
(incf (arg-state-fp-registers state) 2)
(my-make-wired-tn 'double-float 'double-reg register)))))
(define-alien-type-method (integer :result-tn) (type state)
(let ((num-results (result-state-num-results state)))
(setf (result-state-num-results state) (1+ num-results))
(multiple-value-bind (ptype reg-sc)
(if (alien-integer-type-signed type)
(values 'signed-byte-32 'signed-reg)
(values 'unsigned-byte-32 'unsigned-reg))
(my-make-wired-tn ptype reg-sc
(result-reg-offset num-results)))))
(define-alien-type-method (system-area-pointer :result-tn) (type state)
(declare (ignore type state))
(my-make-wired-tn 'system-area-pointer 'sap-reg nargs-offset))
#!+arm-softfp
(define-alien-type-method (single-float :result-tn) (type state)
(declare (ignore type state))
(my-make-wired-tn 'single-float 'unsigned-reg nargs-offset))
#!-arm-softfp
(define-alien-type-method (single-float :result-tn) (type state)
(declare (ignore type state))
(my-make-wired-tn 'single-float 'single-reg 0))
#!+arm-softfp
(define-alien-type-method (double-float :result-tn) (type state)
(declare (ignore type state))
(list (my-make-wired-tn 'unsigned-byte-32 'unsigned-reg nargs-offset)
(my-make-wired-tn 'unsigned-byte-32 'unsigned-reg nl3-offset)
'move-int-args-to-double))
#!-arm-softfp
(define-alien-type-method (double-float :result-tn) (type state)
(declare (ignore type state))
(my-make-wired-tn 'double-float 'double-reg 0))
(define-alien-type-method (values :result-tn) (type state)
(let ((values (alien-values-type-values type)))
(when (> (length values) 2)
(error "Too many result values from c-call."))
(mapcar (lambda (type)
(invoke-alien-type-method :result-tn type state))
values)))
(defun make-call-out-tns (type)
(let ((arg-state (make-arg-state)))
(collect ((arg-tns))
(dolist (arg-type (alien-fun-type-arg-types type))
(arg-tns (invoke-alien-type-method :arg-tn arg-type arg-state)))
(values (make-normal-tn *fixnum-primitive-type*)
(* (arg-state-stack-frame-size arg-state) n-word-bytes)
(arg-tns)
(invoke-alien-type-method :result-tn
(alien-fun-type-result-type type)
(make-result-state))))))
(define-vop (foreign-symbol-sap)
(:translate foreign-symbol-sap)
(:policy :fast-safe)
(:args)
(:arg-types (:constant simple-string))
(:info foreign-symbol)
(:temporary (:sc interior-reg) lip)
(:results (res :scs (sap-reg)))
(:result-types system-area-pointer)
(:generator 2
(let ((fixup-label (gen-label)))
(inst load-from-label res lip fixup-label)
(assemble (*elsewhere*)
(emit-label fixup-label)
(inst word (make-fixup foreign-symbol :foreign))))))
#!+linkage-table
(define-vop (foreign-symbol-dataref-sap)
(:translate foreign-symbol-dataref-sap)
(:policy :fast-safe)
(:args)
(:arg-types (:constant simple-string))
(:info foreign-symbol)
(:temporary (:sc interior-reg) lip)
(:results (res :scs (sap-reg)))
(:result-types system-area-pointer)
(:generator 2
(let ((fixup-label (gen-label)))
(inst load-from-label res lip fixup-label)
(inst ldr res (@ res))
(assemble (*elsewhere*)
(emit-label fixup-label)
(inst word (make-fixup foreign-symbol :foreign-dataref))))))
(define-vop (call-out)
(:args (function :scs (sap-reg sap-stack))
(args :more t))
(:results (results :more t))
(:ignore args results)
(:save-p t)
(:temporary (:sc any-reg :offset r8-offset
:from (:argument 0) :to (:result 0)) cfunc)
(:temporary (:sc control-stack :offset nfp-save-offset) nfp-save)
(:temporary (:sc any-reg) temp)
(:temporary (:sc interior-reg) lip)
(:vop-var vop)
(:generator 0
(let ((call-into-c-fixup (gen-label))
(cur-nfp (current-nfp-tn vop)))
(assemble (*elsewhere*)
(emit-label call-into-c-fixup)
(inst word (make-fixup "call_into_c" :foreign)))
(when cur-nfp
(store-stack-tn nfp-save cur-nfp))
(inst load-from-label temp lip call-into-c-fixup)
(sc-case function
(sap-reg (move cfunc function))
(sap-stack
(load-stack-offset cfunc cur-nfp function)))
(inst blx temp)
(when cur-nfp
(load-stack-tn cur-nfp nfp-save)))))
(define-vop (alloc-number-stack-space)
(:info amount)
(:result-types system-area-pointer)
(:results (result :scs (sap-reg any-reg)))
(:generator 0
(unless (zerop amount)
(let ((delta (logandc2 (+ amount +number-stack-alignment-mask+)
+number-stack-alignment-mask+)))
(composite-immediate-instruction sub nsp-tn nsp-tn delta)
(move result nsp-tn)))))
(define-vop (dealloc-number-stack-space)
(:info amount)
(:policy :fast-safe)
(:generator 0
(unless (zerop amount)
(let ((delta (logandc2 (+ amount +number-stack-alignment-mask+)
+number-stack-alignment-mask+)))
(composite-immediate-instruction add nsp-tn nsp-tn delta)))))
;;;
#!+arm-softfp
(define-vop (move-double-to-int-args)
(:args (double :scs (double-reg)))
(:results (lo-bits :scs (unsigned-reg))
(hi-bits :scs (unsigned-reg)))
(:arg-types double-float)
(:result-types unsigned-num unsigned-num)
(:policy :fast-safe)
(:generator 1
(inst fmrrd lo-bits hi-bits double)))
#!+arm-softfp
(define-vop (move-int-args-to-double)
(:args (lo-bits :scs (unsigned-reg))
(hi-bits :scs (unsigned-reg)))
(:results (double :scs (double-reg)))
(:arg-types unsigned-num unsigned-num)
(:result-types double-float)
(:policy :fast-safe)
(:generator 1
(inst fmdrr double lo-bits hi-bits)))
;;; long-long support
(deftransform %alien-funcall ((function type &rest args) * * :node node)
(aver (sb!c::constant-lvar-p type))
(let* ((type (sb!c::lvar-value type))
(env (sb!c::node-lexenv node))
(arg-types (alien-fun-type-arg-types type))
(result-type (alien-fun-type-result-type type)))
(aver (= (length arg-types) (length args)))
(if (or (some (lambda (type)
(and (alien-integer-type-p type)
(> (sb!alien::alien-integer-type-bits type) 32)))
arg-types)
(and (alien-integer-type-p result-type)
(> (sb!alien::alien-integer-type-bits result-type) 32)))
(collect ((new-args) (lambda-vars) (new-arg-types))
(loop with i = 0
for type in arg-types
for arg = (gensym)
do
(lambda-vars arg)
(cond ((and (alien-integer-type-p type)
(> (sb!alien::alien-integer-type-bits type) 32))
(when (oddp i)
;; long-long is only passed in pairs of r0-r1 and r2-r3,
;; and the stack is double-word aligned
(incf i)
(new-args 0)
(new-arg-types (parse-alien-type '(signed 8) env)))
(incf i 2)
(new-args `(logand ,arg #xffffffff))
(new-args `(ash ,arg -32))
(new-arg-types (parse-alien-type '(unsigned 32) env))
(if (alien-integer-type-signed type)
(new-arg-types (parse-alien-type '(signed 32) env))
(new-arg-types (parse-alien-type '(unsigned 32) env))))
(t
(incf i (cond ((or (alien-double-float-type-p type)
#!-arm-softfp (alien-single-float-type-p type))
#!+arm-softfp 2
#!-arm-softfp 0)
(t
1)))
(new-args arg)
(new-arg-types type))))
(cond ((and (alien-integer-type-p result-type)
(> (sb!alien::alien-integer-type-bits result-type) 32))
(let ((new-result-type
(let ((sb!alien::*values-type-okay* t))
(parse-alien-type
(if (alien-integer-type-signed result-type)
'(values (unsigned 32) (signed 32))
'(values (unsigned 32) (unsigned 32)))
env))))
`(lambda (function type ,@(lambda-vars))
(declare (ignore type))
(multiple-value-bind (low high)
(%alien-funcall function
',(make-alien-fun-type
:arg-types (new-arg-types)
:result-type new-result-type)
,@(new-args))
(logior low (ash high 32))))))
(t
`(lambda (function type ,@(lambda-vars))
(declare (ignore type))
(%alien-funcall function
',(make-alien-fun-type
:arg-types (new-arg-types)
:result-type result-type)
,@(new-args))))))
(sb!c::give-up-ir1-transform))))
;;; Callback
#-sb-xc-host
(defun alien-callback-accessor-form (type sap offset)
(let ((parsed-type type))
(if (alien-integer-type-p parsed-type)
(let ((bits (sb!alien::alien-integer-type-bits parsed-type)))
(let ((byte-offset
(cond ((< bits n-word-bits)
(- n-word-bytes
(ceiling bits n-byte-bits)))
(t 0))))
`(deref (sap-alien (sap+ ,sap
,(+ byte-offset offset))
(* ,type)))))
`(deref (sap-alien (sap+ ,sap ,offset) (* ,type))))))
#-sb-xc-host
(defun alien-callback-assembler-wrapper (index result-type argument-types)
(flet ((make-tn (offset &optional (sc-name 'any-reg))
(make-random-tn :kind :normal
:sc (sc-or-lose sc-name)
:offset offset)))
(let* ((segment (make-segment))
;; How many arguments have been copied
(arg-count 0)
;; How many arguments have been copied from the stack
(stack-argument-count 0)
(r0-tn (make-tn 0))
(r1-tn (make-tn 1))
(r2-tn (make-tn 2))
(r3-tn (make-tn 3))
(r4-tn (make-tn 4))
(temp-tn (make-tn 5))
(nsp-save-tn (make-tn 6))
#!-arm-softfp
(fp-registers 0)
(gprs (list r0-tn r1-tn r2-tn r3-tn))
(frame-size
(loop for type in argument-types
sum (* n-word-bytes
(if (or (alien-double-float-type-p type)
(and (alien-integer-type-p type)
(eql (alien-type-bits type) 64)))
2
1)))))
(setf frame-size (logandc2 (+ frame-size +number-stack-alignment-mask+)
+number-stack-alignment-mask+))
(assemble (segment)
(emit-word segment #xe92d4ff8) ;; stmfd sp!, {r3-r11, lr}
(move nsp-save-tn nsp-tn)
;; Make room on the stack for arguments.
(when (plusp frame-size)
(inst sub nsp-tn nsp-tn frame-size))
;; Copy arguments
(dolist (type argument-types)
(let ((target-tn (@ nsp-tn (* arg-count n-word-bytes)))
;; A TN pointing to the stack location that contains
;; the next argument passed on the stack.
;; 10 is the amount of registers saved by stmfd above.
(stack-arg-tn (@ nsp-save-tn (* (+ 10 stack-argument-count)
n-word-bytes))))
(cond ((or (and (alien-integer-type-p type)
(not (eql (alien-type-bits type) 64)))
(alien-pointer-type-p type)
(alien-type-= #.(parse-alien-type 'system-area-pointer nil)
type)
#!+arm-softfp
(alien-single-float-type-p type))
(let ((gpr (pop gprs)))
(cond (gpr
(inst str gpr target-tn))
(t
(incf stack-argument-count)
(inst ldr temp-tn stack-arg-tn)
(inst str temp-tn target-tn))))
(incf arg-count))
((or #!+arm-softfp
(alien-double-float-type-p type)
;; long-long
(alien-integer-type-p type))
(let ((left (length gprs)))
(case left
((2 3 4)
(when (= left 3)
(pop gprs))
(inst str (pop gprs) (@ nsp-tn (* arg-count n-word-bytes)))
(incf arg-count)
(inst str (pop gprs) (@ nsp-tn (* arg-count n-word-bytes)))
(incf arg-count))
(t
(pop gprs)
;; two-word aligned
(setf stack-argument-count
(logandc2 (+ stack-argument-count 1) 1))
(inst ldr temp-tn (@ nsp-save-tn (* (+ 10 stack-argument-count)
n-word-bytes)))
(inst str temp-tn (@ nsp-tn (* arg-count n-word-bytes)))
(incf arg-count)
(inst ldr temp-tn (@ nsp-save-tn (* (+ 11 stack-argument-count)
n-word-bytes)))
(inst str temp-tn (@ nsp-tn (* arg-count n-word-bytes)))
(incf stack-argument-count 2)
(incf arg-count)))))
#!-arm-softfp
((alien-double-float-type-p type)
(setf fp-registers (logandc2 (+ fp-registers 1) 1))
(cond
((> fp-registers 15)
;; align
(setf stack-argument-count
(logandc2 (+ stack-argument-count 1) 1))
(inst ldr temp-tn (@ nsp-save-tn (* (+ 10 stack-argument-count)
n-word-bytes)))
(inst str temp-tn (@ nsp-tn (* arg-count n-word-bytes)))
(incf arg-count)
(inst ldr temp-tn (@ nsp-save-tn (* (+ 11 stack-argument-count)
n-word-bytes)))
(inst str temp-tn (@ nsp-tn (* arg-count n-word-bytes)))
(incf stack-argument-count 2)
(incf arg-count))
(t
(inst fstd (make-tn fp-registers 'double-reg) target-tn)
(incf fp-registers 2)
(incf arg-count 2))))
#!-arm-softfp
((alien-single-float-type-p type)
(cond ((> fp-registers 15)
(incf stack-argument-count)
(inst ldr temp-tn stack-arg-tn)
(inst str temp-tn target-tn))
(t
(inst fsts (make-tn fp-registers 'single-reg) target-tn)
(incf fp-registers 1)))
(incf arg-count 1))
(t
(bug "Unknown alien floating point type: ~S" type)))))
;; arg0 to FUNCALL3 (function)
;;
;; Indirect the access to ENTER-ALIEN-CALLBACK through
;; the symbol-value slot of SB-ALIEN::*ENTER-ALIEN-CALLBACK*
;; to ensure it'll work even if the GC moves ENTER-ALIEN-CALLBACK.
;; Skip any SB-THREAD TLS magic, since we don't expect anyone
;; to rebind the variable. -- JES, 2006-01-01
(load-immediate-word r0-tn (+ nil-value (static-symbol-offset
'sb!alien::*enter-alien-callback*)))
(loadw r0-tn r0-tn symbol-value-slot other-pointer-lowtag)
;; arg0 to ENTER-ALIEN-CALLBACK (trampoline index)
(inst mov r1-tn (fixnumize index))
;; arg1 to ENTER-ALIEN-CALLBACK (pointer to argument vector)
(inst mov r2-tn nsp-tn)
;; add room on stack for return value
(inst sub nsp-tn nsp-tn 8)
;; arg2 to ENTER-ALIEN-CALLBACK (pointer to return value)
(inst mov r3-tn nsp-tn)
;; Call
(load-immediate-word r4-tn (foreign-symbol-address "funcall3"))
(inst blx r4-tn)
;; Result now on top of stack, put it in the right register
(cond
((or (and (alien-integer-type-p result-type)
(not (eql (alien-type-bits result-type) 64)))
(alien-pointer-type-p result-type)
(alien-type-= #.(parse-alien-type 'system-area-pointer nil)
result-type)
#!+arm-softfp
(alien-single-float-type-p result-type))
(loadw r0-tn nsp-tn))
((or #!+arm-softfp (alien-double-float-type-p result-type)
;; long-long
(alien-integer-type-p result-type))
(loadw r0-tn nsp-tn)
(loadw r1-tn nsp-tn 1))
#!-arm-softfp
((alien-single-float-type-p result-type)
(inst flds (make-tn 0 'single-reg) (@ nsp-tn)))
#!-arm-softfp
((alien-double-float-type-p result-type)
(inst fldd (make-tn 0 'double-reg) (@ nsp-tn)))
((alien-void-type-p result-type))
(t
(error "Unrecognized alien type: ~A" result-type)))
(move nsp-tn nsp-save-tn)
(emit-word segment #xe8bd4ff8) ;; ldmfd sp!, {r3-r11, lr}
(inst bx lr-tn))
(finalize-segment segment)
;; Now that the segment is done, convert it to a static
;; vector we can point foreign code to.
(let* ((buffer (sb!assem::segment-buffer segment))
(vector (make-static-vector (length buffer)
:element-type '(unsigned-byte 8)
:initial-contents buffer))
(sap (vector-sap vector)))
(alien-funcall
(extern-alien "os_flush_icache"
(function void
system-area-pointer
unsigned-long))
sap (length buffer))
vector))))

1223
src/compiler/arm64/call.lisp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,342 @@
;;;; the VM definition of various primitive memory access VOPs for the
;;;; ARM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; Data object ref/set stuff.
(define-vop (slot)
(:args (object :scs (descriptor-reg)))
(:info name offset lowtag)
(:ignore name)
(:results (result :scs (descriptor-reg any-reg)))
(:generator 1
(loadw result object offset lowtag)))
(define-vop (set-slot)
(:args (object :scs (descriptor-reg))
(value :scs (descriptor-reg any-reg)))
(:info name offset lowtag)
(:ignore name)
(:results)
(:generator 1
(storew value object offset lowtag)))
(define-vop (init-slot set-slot))
;;;; Symbol hacking VOPs:
;;; The compiler likes to be able to directly SET symbols.
;;;
(define-vop (set cell-set)
(:variant symbol-value-slot other-pointer-lowtag))
;;; Do a cell ref with an error check for being unbound.
;;;
(define-vop (checked-cell-ref)
(:args (object :scs (descriptor-reg) :target obj-temp))
(:results (value :scs (descriptor-reg any-reg)))
(:policy :fast-safe)
(:vop-var vop)
(:save-p :compute-only)
(:temporary (:scs (descriptor-reg) :from (:argument 0)) obj-temp))
;;; With Symbol-Value, we check that the value isn't the trap object. So
;;; Symbol-Value of NIL is NIL.
;;;
(define-vop (symbol-value checked-cell-ref)
(:translate symbol-value)
(:generator 9
(move obj-temp object)
(loadw value obj-temp symbol-value-slot other-pointer-lowtag)
(let ((err-lab (generate-error-code vop 'unbound-symbol-error obj-temp)))
(inst cmp value unbound-marker-widetag)
(inst b :eq err-lab))))
;;; Like CHECKED-CELL-REF, only we are a predicate to see if the cell is bound.
(define-vop (boundp-frob)
(:args (object :scs (descriptor-reg)))
(:conditional)
(:info target not-p)
(:policy :fast-safe)
(:temporary (:scs (descriptor-reg)) value))
(define-vop (boundp boundp-frob)
(:translate boundp)
(:generator 9
(loadw value object symbol-value-slot other-pointer-lowtag)
(inst cmp value unbound-marker-widetag)
(inst b (if not-p :eq :ne) target)))
(define-vop (fast-symbol-value cell-ref)
(:variant symbol-value-slot other-pointer-lowtag)
(:policy :fast)
(:translate symbol-value))
(define-vop (symbol-hash)
(:policy :fast-safe)
(:translate symbol-hash)
(:args (symbol :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) temp)
(:results (res :scs (any-reg)))
(:result-types positive-fixnum)
(:generator 2
;; The symbol-hash slot of NIL holds NIL because it is also the
;; cdr slot, so we have to strip off the two low bits to make sure
;; it is a fixnum. The lowtag selection magic that is required to
;; ensure this is explained in the comment in objdef.lisp
(loadw temp symbol symbol-hash-slot other-pointer-lowtag)
(inst bic res temp fixnum-tag-mask)))
;;; On unithreaded builds these are just copies of the non-global versions.
(define-vop (%set-symbol-global-value set))
(define-vop (symbol-global-value symbol-value)
(:translate symbol-global-value))
(define-vop (fast-symbol-global-value fast-symbol-value)
(:translate symbol-global-value))
;;;; Fdefinition (fdefn) objects.
(define-vop (fdefn-fun cell-ref)
(:variant fdefn-fun-slot other-pointer-lowtag))
(define-vop (safe-fdefn-fun)
(:args (object :scs (descriptor-reg) :target obj-temp))
(:results (value :scs (descriptor-reg any-reg)))
(:vop-var vop)
(:save-p :compute-only)
(:temporary (:scs (descriptor-reg) :from (:argument 0)) obj-temp)
(:generator 10
(move obj-temp object)
(loadw value obj-temp fdefn-fun-slot other-pointer-lowtag)
(inst cmp value null-tn)
(let ((err-lab (generate-error-code vop 'undefined-fun-error obj-temp)))
(inst b :eq err-lab))))
(define-vop (set-fdefn-fun)
(:policy :fast-safe)
(:translate (setf fdefn-fun))
(:args (function :scs (descriptor-reg) :target result)
(fdefn :scs (descriptor-reg)))
(:temporary (:scs (interior-reg)) lip)
(:temporary (:scs (non-descriptor-reg)) type)
(:results (result :scs (descriptor-reg)))
(:generator 38
(let ((closure-tramp-fixup (gen-label)))
(assemble (*elsewhere*)
(emit-label closure-tramp-fixup)
(inst word (make-fixup "closure_tramp" :foreign)))
(load-type type function (- fun-pointer-lowtag))
(inst cmp type simple-fun-header-widetag)
(inst mov :eq lip function)
(inst load-from-label :ne lip lip closure-tramp-fixup)
(storew lip fdefn fdefn-raw-addr-slot other-pointer-lowtag)
(storew function fdefn fdefn-fun-slot other-pointer-lowtag)
(move result function))))
(define-vop (fdefn-makunbound)
(:policy :fast-safe)
(:translate fdefn-makunbound)
(:args (fdefn :scs (descriptor-reg) :target result))
(:temporary (:scs (non-descriptor-reg)) temp)
(:temporary (:scs (interior-reg)) lip)
(:results (result :scs (descriptor-reg)))
(:generator 38
(let ((undefined-tramp-fixup (gen-label)))
(assemble (*elsewhere*)
(emit-label undefined-tramp-fixup)
(inst word (make-fixup "undefined_tramp" :foreign)))
(storew null-tn fdefn fdefn-fun-slot other-pointer-lowtag)
(inst load-from-label temp lip undefined-tramp-fixup)
(storew temp fdefn fdefn-raw-addr-slot other-pointer-lowtag)
(move result fdefn))))
;;;; Binding and Unbinding.
;;; BIND -- Establish VAL as a binding for SYMBOL. Save the old value and
;;; the symbol on the binding stack and stuff the new value into the
;;; symbol.
(define-vop (bind)
(:args (val :scs (any-reg descriptor-reg))
(symbol :scs (descriptor-reg)))
(:temporary (:scs (descriptor-reg)) value-temp)
(:temporary (:scs (any-reg)) bsp-temp)
(:generator 5
(loadw value-temp symbol symbol-value-slot other-pointer-lowtag)
(load-symbol-value bsp-temp *binding-stack-pointer*)
(inst add bsp-temp bsp-temp (* 2 n-word-bytes))
(store-symbol-value bsp-temp *binding-stack-pointer*)
(storew value-temp bsp-temp (- binding-value-slot binding-size))
(storew symbol bsp-temp (- binding-symbol-slot binding-size))
(storew val symbol symbol-value-slot other-pointer-lowtag)))
(define-vop (unbind)
(:temporary (:scs (descriptor-reg)) symbol value)
(:temporary (:scs (any-reg)) bsp-temp)
(:temporary (:scs (any-reg)) zero-temp)
(:generator 0
(inst mov zero-temp 0)
(load-symbol-value bsp-temp *binding-stack-pointer*)
(loadw symbol bsp-temp (- binding-symbol-slot binding-size))
(loadw value bsp-temp (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
(storew zero-temp bsp-temp (- binding-symbol-slot binding-size))
(storew zero-temp bsp-temp (- binding-value-slot binding-size))
(inst sub bsp-temp bsp-temp (* 2 n-word-bytes))
(store-symbol-value bsp-temp *binding-stack-pointer*)))
(define-vop (unbind-to-here)
(:args (arg :scs (descriptor-reg any-reg) :target where))
(:temporary (:scs (any-reg) :from (:argument 0)) where)
(:temporary (:scs (descriptor-reg)) symbol value)
(:temporary (:scs (any-reg)) bsp-temp zero-temp)
(:generator 0
(load-symbol-value bsp-temp *binding-stack-pointer*)
(inst mov zero-temp 0)
(move where arg)
(inst cmp where bsp-temp)
(inst b :eq DONE)
LOOP
(loadw symbol bsp-temp (- binding-symbol-slot binding-size))
(inst cmp symbol 0)
(loadw value bsp-temp (- binding-value-slot binding-size) 0 :ne)
(storew value symbol symbol-value-slot other-pointer-lowtag :ne)
(storew zero-temp bsp-temp (- binding-symbol-slot binding-size) 0 :ne)
(storew zero-temp bsp-temp (- binding-value-slot binding-size))
(inst sub bsp-temp bsp-temp (* 2 n-word-bytes))
(inst cmp where bsp-temp)
(inst b :ne LOOP)
DONE
(store-symbol-value bsp-temp *binding-stack-pointer*)))
;;;; Closure indexing.
(define-full-reffer closure-index-ref *
closure-info-offset fun-pointer-lowtag
(descriptor-reg any-reg) * %closure-index-ref)
(define-full-setter set-funcallable-instance-info *
funcallable-instance-info-offset fun-pointer-lowtag
(descriptor-reg any-reg null) * %set-funcallable-instance-info)
(define-full-reffer funcallable-instance-info *
funcallable-instance-info-offset fun-pointer-lowtag
(descriptor-reg any-reg) * %funcallable-instance-info)
(define-vop (closure-ref slot-ref)
(:variant closure-info-offset fun-pointer-lowtag))
(define-vop (closure-init slot-set)
(:variant closure-info-offset fun-pointer-lowtag))
(define-vop (closure-init-from-fp)
(:args (object :scs (descriptor-reg)))
(:info offset)
(:generator 4
(storew cfp-tn object (+ closure-info-offset offset) fun-pointer-lowtag)))
;;;; Value Cell hackery.
(define-vop (value-cell-ref cell-ref)
(:variant value-cell-value-slot other-pointer-lowtag))
(define-vop (value-cell-set cell-set)
(:variant value-cell-value-slot other-pointer-lowtag))
;;;; Instance hackery:
(define-vop (instance-length)
(:policy :fast-safe)
(:translate %instance-length)
(:args (struct :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) temp)
(:results (res :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:generator 4
(loadw temp struct 0 instance-pointer-lowtag)
(inst mov res (lsr temp n-widetag-bits))))
(define-full-reffer instance-index-ref * instance-slots-offset
instance-pointer-lowtag (descriptor-reg any-reg) * %instance-ref)
(define-full-setter instance-index-set * instance-slots-offset
instance-pointer-lowtag (descriptor-reg any-reg null) * %instance-set)
;;;; Code object frobbing.
(define-full-reffer code-header-ref * 0 other-pointer-lowtag
(descriptor-reg any-reg) * code-header-ref)
(define-full-setter code-header-set * 0 other-pointer-lowtag
(descriptor-reg any-reg null) * code-header-set)
;;;; raw instance slot accessors
(macrolet
((define-raw-slot-vops (name ref-inst set-inst value-primtype value-sc
&key (width 1) use-lip (move-macro 'move))
(labels ((emit-generator (instruction move-result)
`((loadw offset object 0 instance-pointer-lowtag)
(inst mov offset (lsr offset n-widetag-bits))
(inst rsb offset index (lsl offset n-fixnum-tag-bits))
(inst sub offset offset (+ (* (- ,width
instance-slots-offset)
n-word-bytes)
instance-pointer-lowtag))
,@(when use-lip
'((inst add lip object offset)))
(inst ,instruction value ,(if use-lip
'(@ lip)
'(@ object offset)))
,@(when move-result
`((,move-macro result value))))))
(let ((ref-vop (symbolicate "RAW-INSTANCE-REF/" name))
(set-vop (symbolicate "RAW-INSTANCE-SET/" name)))
`(progn
(define-vop (,ref-vop)
(:translate ,(symbolicate "%" ref-vop))
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg)))
(:arg-types * positive-fixnum)
(:results (value :scs (,value-sc)))
(:result-types ,value-primtype)
(:temporary (:scs (non-descriptor-reg)) offset)
,@(when use-lip '((:temporary (:scs (interior-reg)) lip)))
(:generator 5 ,@(emit-generator ref-inst nil)))
(define-vop (,set-vop)
(:translate ,(symbolicate "%" set-vop))
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg))
(value :scs (,value-sc) :target result))
(:arg-types * positive-fixnum ,value-primtype)
(:results (result :scs (,value-sc)))
(:result-types ,value-primtype)
(:temporary (:scs (non-descriptor-reg)) offset)
,@(when use-lip '((:temporary (:scs (interior-reg)) lip)))
(:generator 5 ,@(emit-generator set-inst t))))))))
(define-raw-slot-vops word ldr str unsigned-num unsigned-reg)
(define-raw-slot-vops single flds fsts single-float single-reg
:use-lip t :move-macro move-single)
(define-raw-slot-vops double fldd fstd double-float double-reg
:use-lip t :width 2 :move-macro move-double)
(define-raw-slot-vops complex-single load-complex-single store-complex-single complex-single-float complex-single-reg
:use-lip t :width 2 :move-macro move-complex-single)
(define-raw-slot-vops complex-double load-complex-double store-complex-double complex-double-float complex-double-reg
:use-lip t :width 4 :move-macro move-complex-double))

View file

@ -0,0 +1,114 @@
;;;; the ARM VM definition of character operations
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; Moves and coercions:
;;; Move a tagged char to an untagged representation.
(define-vop (move-to-character)
(:args (x :scs (any-reg descriptor-reg)))
(:results (y :scs (character-reg)))
(:note "character untagging")
(:generator 1
(inst mov y (lsr x n-widetag-bits))))
(define-move-vop move-to-character :move
(any-reg descriptor-reg) (character-reg))
;;; Move an untagged char to a tagged representation.
(define-vop (move-from-character)
(:args (x :scs (character-reg)))
(:results (y :scs (any-reg descriptor-reg)))
(:note "character tagging")
(:generator 1
(inst mov y (lsl x n-widetag-bits))
(inst orr y y character-widetag)))
(define-move-vop move-from-character :move
(character-reg) (any-reg descriptor-reg))
;;; Move untagged character values.
(define-vop (character-move)
(:args (x :target y
:scs (character-reg)
:load-if (not (location= x y))))
(:results (y :scs (character-reg)
:load-if (not (location= x y))))
(:note "character move")
(:effects)
(:affected)
(:generator 0
(move y x)))
(define-move-vop character-move :move
(character-reg) (character-reg))
;;; Move untagged character arguments/return-values.
(define-vop (move-character-arg)
(:args (x :target y
:scs (character-reg))
(fp :scs (any-reg)
:load-if (not (sc-is y character-reg))))
(:results (y))
(:note "character arg move")
(:generator 0
(sc-case y
(character-reg
(move y x))
(character-stack
(store-stack-offset x fp y)))))
(define-move-vop move-character-arg :move-arg
(any-reg character-reg) (character-reg))
;;; Use standard MOVE-ARG + coercion to move an untagged character
;;; to a descriptor passing location.
(define-move-vop move-arg :move-arg
(character-reg) (any-reg descriptor-reg))
;;;; Other operations:
(define-vop (char-code)
(:translate char-code)
(:policy :fast-safe)
(:args (ch :scs (character-reg) :target res))
(:arg-types character)
(:results (res :scs (any-reg)))
(:result-types positive-fixnum)
(:generator 1
(inst mov res (lsl ch n-fixnum-tag-bits))))
(define-vop (code-char)
(:translate code-char)
(:policy :fast-safe)
(:args (code :scs (any-reg) :target res))
(:arg-types positive-fixnum)
(:results (res :scs (character-reg)))
(:result-types character)
(:generator 1
(inst mov res (lsr code n-fixnum-tag-bits))))
(define-vop (character-compare)
(:args (x :scs (character-reg))
(y :scs (character-reg)))
(:arg-types character character)
(:policy :fast-safe)
(:note "inline comparison")
(:generator 3
(inst cmp x y)))
(define-vop (fast-char=/character character-compare)
(:translate char=)
(:conditional :eq))
(define-vop (fast-char>/character character-compare)
(:translate char>)
(:conditional :gt))
(define-vop (fast-char</character character-compare)
(:translate char<)
(:conditional :lt))

View file

@ -0,0 +1,105 @@
;;;; ARM compiler support for the debugger
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(define-vop (debug-cur-sp)
(:translate sb!di::current-sp)
(:policy :fast-safe)
(:results (res :scs (sap-reg)))
(:result-types system-area-pointer)
(:generator 1
(load-csp res)))
(define-vop (debug-cur-fp)
(:translate sb!di::current-fp)
(:policy :fast-safe)
(:results (res :scs (sap-reg)))
(:result-types system-area-pointer)
(:generator 1
(move res cfp-tn)))
(define-vop (read-control-stack)
(:translate sb!kernel:stack-ref)
(:policy :fast-safe)
(:args (sap :scs (sap-reg))
(offset :scs (any-reg)))
(:arg-types system-area-pointer positive-fixnum)
(:results (result :scs (descriptor-reg)))
(:result-types *)
(:generator 5
(inst ldr result (@ sap offset))))
(define-vop (write-control-stack)
(:translate sb!kernel:%set-stack-ref)
(:policy :fast-safe)
(:args (sap :scs (sap-reg))
(offset :scs (any-reg))
(value :scs (descriptor-reg) :target result))
(:arg-types system-area-pointer positive-fixnum *)
(:results (result :scs (descriptor-reg)))
(:result-types *)
(:generator 5
(inst str value (@ sap offset))
(move result value)))
(define-vop (code-from-mumble)
(:policy :fast-safe)
(:args (thing :scs (descriptor-reg)))
(:results (code :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) temp)
(:variant-vars lowtag)
(:generator 5
(loadw temp thing 0 lowtag)
(inst mov temp (lsr temp (- n-widetag-bits word-shift)))
(inst cmp temp 0)
(unless (= lowtag other-pointer-lowtag)
(inst sub :ne temp temp (- other-pointer-lowtag lowtag)))
(inst sub :ne code thing temp)
(inst mov :eq code null-tn)))
(define-vop (code-from-lra code-from-mumble)
(:translate sb!di::lra-code-header)
(:variant other-pointer-lowtag))
(define-vop (code-from-fun code-from-mumble)
(:translate sb!di::fun-code-header)
(:variant fun-pointer-lowtag))
(define-vop (%make-lisp-obj)
(:policy :fast-safe)
(:translate %make-lisp-obj)
(:args (value :scs (unsigned-reg) :target result))
(:arg-types unsigned-num)
(:results (result :scs (descriptor-reg)))
(:generator 1
(move result value)))
(define-vop (get-lisp-obj-address)
(:policy :fast-safe)
(:translate sb!di::get-lisp-obj-address)
(:args (thing :scs (descriptor-reg) :target result))
(:results (result :scs (unsigned-reg)))
(:result-types unsigned-num)
(:generator 1
(move result thing)))
(define-vop (fun-word-offset)
(:policy :fast-safe)
(:translate sb!di::fun-word-offset)
(:args (fun :scs (descriptor-reg)))
(:results (res :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:generator 5
(loadw res fun 0 fun-pointer-lowtag)
(inst mov res (lsr res n-widetag-bits))))

View file

@ -0,0 +1,805 @@
;;;; floating point support for the ARM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; Move functions:
(define-move-fun (load-single 1) (vop x y)
((single-stack) (single-reg))
(inst flds y (@ (current-nfp-tn vop) (* (tn-offset x) n-word-bytes))))
(define-move-fun (store-single 1) (vop x y)
((single-reg) (single-stack))
(inst fsts x (@ (current-nfp-tn vop) (* (tn-offset y) n-word-bytes))))
(define-move-fun (load-double 2) (vop x y)
((double-stack) (double-reg))
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset x) n-word-bytes)))
(inst fldd y (@ nfp offset))))
(define-move-fun (store-double 2) (vop x y)
((double-reg) (double-stack))
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset y) n-word-bytes)))
(inst fstd x (@ nfp offset))))
;;;; Move VOPs:
(macrolet ((frob (vop sc move-macro)
`(progn
(define-vop (,vop)
(:args (x :scs (,sc)
:target y
:load-if (not (location= x y))))
(:results (y :scs (,sc)
:load-if (not (location= x y))))
(:note "float move")
(:generator 0
(,move-macro y x)))
(define-move-vop ,vop :move (,sc) (,sc)))))
(frob single-move single-reg move-single)
(frob double-move double-reg move-double))
(define-vop (move-from-float)
(:args (x :to :save))
(:results (y))
(:note "float to pointer coercion")
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:temporary (:sc interior-reg) lip)
(:variant-vars double-p size type data)
(:generator 13
(with-fixed-allocation (y pa-flag type size)
(inst sub lip y other-pointer-lowtag)
(if double-p
(inst fstd x (@ lip (* data n-word-bytes)))
(inst fsts x (@ lip (* data n-word-bytes)))))))
(macrolet ((frob (name sc &rest args)
`(progn
(define-vop (,name move-from-float)
(:args (x :scs (,sc) :to :save))
(:results (y :scs (descriptor-reg)))
(:variant ,@args))
(define-move-vop ,name :move (,sc) (descriptor-reg)))))
(frob move-from-single single-reg
nil single-float-size single-float-widetag single-float-value-slot)
(frob move-from-double double-reg
t double-float-size double-float-widetag double-float-value-slot))
(macrolet ((frob (name sc double-p value)
`(progn
(define-vop (,name)
(:args (x :scs (descriptor-reg)))
(:results (y :scs (,sc)))
(:temporary (:sc interior-reg) lip)
(:note "pointer to float coercion")
(:generator 2
(inst sub lip x other-pointer-lowtag)
(inst ,(if double-p 'fldd 'flds) y
(@ lip (* ,value n-word-bytes)))))
(define-move-vop ,name :move (descriptor-reg) (,sc)))))
(frob move-to-single single-reg nil single-float-value-slot)
(frob move-to-double double-reg t double-float-value-slot))
(macrolet ((frob (name sc stack-sc double-p)
`(progn
(define-vop (,name)
(:args (x :scs (,sc) :target y)
(nfp :scs (any-reg)
:load-if (not (sc-is y ,sc))))
(:results (y))
(:note "float arg move")
(:generator ,(if double-p 2 1)
(sc-case y
(,sc
(,(if double-p 'move-double 'move-single) y x))
(,stack-sc
(let ((offset (* (tn-offset y) n-word-bytes)))
(inst ,(if double-p 'fstd 'fsts) x (@ nfp offset)))))))
(define-move-vop ,name :move-arg
(,sc descriptor-reg) (,sc)))))
(frob move-single-float-arg single-reg single-stack nil)
(frob move-double-float-arg double-reg double-stack t))
;;;; Complex float move functions
(defun complex-single-reg-real-tn (x)
(make-random-tn :kind :normal :sc (sc-or-lose 'single-reg)
:offset (tn-offset x)))
(defun complex-single-reg-imag-tn (x)
(make-random-tn :kind :normal :sc (sc-or-lose 'single-reg)
:offset (1+ (tn-offset x))))
(defun complex-double-reg-real-tn (x)
(make-random-tn :kind :normal :sc (sc-or-lose 'double-reg)
:offset (tn-offset x)))
(defun complex-double-reg-imag-tn (x)
(make-random-tn :kind :normal :sc (sc-or-lose 'double-reg)
:offset (+ 2 (tn-offset x))))
(define-move-fun (load-complex-single 2) (vop x y)
((complex-single-stack) (complex-single-reg))
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset x) n-word-bytes)))
(inst add lr-tn nfp offset)
(inst load-complex-single y (@ lr-tn))))
(define-move-fun (store-complex-single 2) (vop x y)
((complex-single-reg) (complex-single-stack))
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset y) n-word-bytes)))
(inst add lr-tn nfp offset)
(inst store-complex-single x (@ lr-tn))))
(define-move-fun (load-complex-double 4) (vop x y)
((complex-double-stack) (complex-double-reg))
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset x) n-word-bytes)))
(inst add lr-tn nfp offset)
(inst load-complex-double y (@ lr-tn))))
(define-move-fun (store-complex-double 4) (vop x y)
((complex-double-reg) (complex-double-stack))
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset y) n-word-bytes)))
(inst add lr-tn nfp offset)
(inst store-complex-double x (@ lr-tn))))
;;;
;;; Complex float register to register moves.
;;;
(define-vop (complex-single-move)
(:args (x :scs (complex-single-reg) :target y
:load-if (not (location= x y))))
(:results (y :scs (complex-single-reg) :load-if (not (location= x y))))
(:note "complex single float move")
(:generator 0
(move-complex-single y x)))
(define-move-vop complex-single-move :move
(complex-single-reg) (complex-single-reg))
(define-vop (complex-double-move)
(:args (x :scs (complex-double-reg)
:target y :load-if (not (location= x y))))
(:results (y :scs (complex-double-reg) :load-if (not (location= x y))))
(:note "complex double float move")
(:generator 0
(move-complex-double y x)))
(define-move-vop complex-double-move :move
(complex-double-reg) (complex-double-reg))
;;;
;;; Move from a complex float to a descriptor register allocating a
;;; new complex float object in the process.
;;;
(define-vop (move-from-complex-single)
(:args (x :scs (complex-single-reg) :to :save))
(:results (y :scs (descriptor-reg)))
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:note "complex single float to pointer coercion")
(:generator 13
(with-fixed-allocation (y pa-flag complex-single-float-widetag
complex-single-float-size)
(inst sub pa-flag y (- other-pointer-lowtag
(* complex-single-float-real-slot
n-word-bytes)))
(inst store-complex-single x (@ pa-flag)))))
(define-move-vop move-from-complex-single :move
(complex-single-reg) (descriptor-reg))
(define-vop (move-from-complex-double)
(:args (x :scs (complex-double-reg) :to :save))
(:results (y :scs (descriptor-reg)))
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:note "complex double float to pointer coercion")
(:generator 13
(with-fixed-allocation (y pa-flag complex-double-float-widetag
complex-double-float-size)
(inst add pa-flag y (- (* complex-double-float-real-slot
n-word-bytes)
other-pointer-lowtag))
(inst store-complex-double x (@ pa-flag)))))
(define-move-vop move-from-complex-double :move
(complex-double-reg) (descriptor-reg))
;;;
;;; Move from a descriptor to a complex float register
;;;
(define-vop (move-to-complex-single)
(:args (x :scs (descriptor-reg)))
(:results (y :scs (complex-single-reg)))
(:temporary (:sc interior-reg) lip)
(:note "pointer to complex float coercion")
(:generator 2
(inst sub lip x (- other-pointer-lowtag
(* complex-single-float-real-slot
n-word-bytes)))
(inst load-complex-single y (@ lip))))
(define-move-vop move-to-complex-single :move
(descriptor-reg) (complex-single-reg))
(define-vop (move-to-complex-double)
(:args (x :scs (descriptor-reg)))
(:results (y :scs (complex-double-reg)))
(:temporary (:sc interior-reg) lip)
(:note "pointer to complex float coercion")
(:generator 2
(inst add lip x (- (* complex-double-float-real-slot
n-word-bytes)
other-pointer-lowtag))
(inst load-complex-double y (@ lip))))
(define-move-vop move-to-complex-double :move
(descriptor-reg) (complex-double-reg))
;;;
;;; Complex float move-arg vop
;;;
(define-vop (move-complex-single-float-arg)
(:args (x :scs (complex-single-reg) :target y)
(nfp :scs (any-reg) :load-if (not (sc-is y complex-single-reg))))
(:results (y))
(:temporary (:sc interior-reg) lip)
(:note "complex single-float arg move")
(:generator 1
(sc-case y
(complex-single-reg
(move-complex-single y x))
(complex-single-stack
(let ((offset (* (tn-offset y) n-word-bytes)))
(inst add lip nfp offset)
(inst store-complex-single x (@ lip)))))))
(define-move-vop move-complex-single-float-arg :move-arg
(complex-single-reg descriptor-reg) (complex-single-reg))
(define-vop (move-complex-double-float-arg)
(:args (x :scs (complex-double-reg) :target y)
(nfp :scs (any-reg) :load-if (not (sc-is y complex-double-reg))))
(:results (y))
(:temporary (:sc interior-reg) lip)
(:note "complex double-float arg move")
(:generator 2
(sc-case y
(complex-double-reg
(move-complex-double y x))
(complex-double-stack
(let ((offset (* (tn-offset y) n-word-bytes)))
(inst add lip nfp offset)
(inst store-complex-double x (@ lip)))))))
(define-move-vop move-complex-double-float-arg :move-arg
(complex-double-reg descriptor-reg) (complex-double-reg))
;;;; Unboxed-to-boxed MOVE-ARG handling:
;; This little gem here says to use the VOP MOVE-ARG to move any float
;; registers to boxed data. MOVE-ARG only takes boxed data as input,
;; which means that the :MOVE VOPs will be used to do the appropriate
;; conversion.
(define-move-vop move-arg :move-arg
(single-reg double-reg complex-single-reg complex-double-reg)
(descriptor-reg))
;;;; Arithmetic VOPs:
(define-vop (float-op)
(:args (x) (y))
(:results (r))
(:policy :fast-safe)
(:note "inline float arithmetic")
(:vop-var vop)
(:save-p :compute-only))
(macrolet ((frob (name sc ptype)
`(define-vop (,name float-op)
(:args (x :scs (,sc))
(y :scs (,sc)))
(:results (r :scs (,sc)))
(:arg-types ,ptype ,ptype)
(:result-types ,ptype))))
(frob single-float-op single-reg single-float)
(frob double-float-op double-reg double-float))
(macrolet ((frob (op sinst sname scost dinst dname dcost)
`(progn
(define-vop (,sname single-float-op)
(:translate ,op)
(:generator ,scost
(inst ,sinst r x y)))
(define-vop (,dname double-float-op)
(:translate ,op)
(:generator ,dcost
(inst ,dinst r x y))))))
(frob + fadds +/single-float 2 faddd +/double-float 2)
(frob - fsubs -/single-float 2 fsubd -/double-float 2)
(frob * fmuls */single-float 4 fmuld */double-float 5)
(frob / fdivs //single-float 12 fdivd //double-float 19))
(macrolet ((frob (name inst translate sc type)
`(define-vop (,name)
(:args (x :scs (,sc)))
(:results (y :scs (,sc)))
(:translate ,translate)
(:policy :fast-safe)
(:arg-types ,type)
(:result-types ,type)
(:note "inline float arithmetic")
(:vop-var vop)
(:save-p :compute-only)
(:generator 1
(note-this-location vop :internal-error)
(inst ,inst y x)))))
(frob abs/single-float fabss abs single-reg single-float)
(frob abs/double-float fabsd abs double-reg double-float)
(frob %negate/single-float fnegs %negate single-reg single-float)
(frob %negate/double-float fnegd %negate double-reg double-float))
(define-vop (fsqrtd)
(:args (x :scs (double-reg)))
(:results (y :scs (double-reg)))
(:translate %sqrt)
(:policy :fast-safe)
(:arg-types double-float)
(:result-types double-float)
(:note "inline float arithmetic")
(:save-p :compute-only)
(:generator 1
(inst fsqrtd y x)))
(define-vop (fsqrts)
(:args (x :scs (single-reg)))
(:results (y :scs (single-reg)))
(:translate %sqrt)
(:policy :fast-safe)
(:arg-types single-float)
(:result-types single-float)
(:note "inline float arithmetic")
(:save-p :compute-only)
(:generator 1
(inst fsqrts y x)))
;;;; Comparison:
(define-vop (float-compare)
(:args (x) (y))
(:variant-vars format is-=)
(:policy :fast-safe)
(:note "inline float comparison")
(:vop-var vop)
(:save-p :compute-only)
(:generator 3
(note-this-location vop :internal-error)
(ecase format
(:single
(if is-=
(inst fcmps x y)
(inst fcmpes x y)))
(:double
(if is-=
(inst fcmpd x y)
(inst fcmped x y))))
(inst fmstat)))
(macrolet ((frob (name sc ptype)
`(define-vop (,name float-compare)
(:args (x :scs (,sc))
(y :scs (,sc)))
(:arg-types ,ptype ,ptype))))
(frob single-float-compare single-reg single-float)
(frob double-float-compare double-reg double-float))
(macrolet ((frob (translate cond sname dname is-=)
`(progn
(define-vop (,sname single-float-compare)
(:translate ,translate)
(:conditional ,cond)
(:variant :single ,is-=))
(define-vop (,dname double-float-compare)
(:translate ,translate)
(:conditional ,cond)
(:variant :double ,is-=)))))
(frob < :mi </single-float </double-float nil)
(frob > :gt >/single-float >/double-float nil)
(frob = :eq eql/single-float eql/double-float t))
(define-vop (float-compare-zero)
(:args (x))
(:info y)
(:ignore y)
(:variant-vars format is-=)
(:policy :fast-safe)
(:note "inline float comparison")
(:vop-var vop)
(:save-p :compute-only)
(:generator 2
(note-this-location vop :internal-error)
(ecase format
(:single
(if is-=
(inst fcmpzs x)
(inst fcmpezs x)))
(:double
(if is-=
(inst fcmpzd x)
(inst fcmpezd x))))
(inst fmstat)))
(macrolet ((frob (name sc ptype constant-type)
`(define-vop (,name float-compare-zero)
(:args (x :scs (,sc)))
(:arg-types ,ptype (:constant, constant-type)))))
(frob single-float-compare-zero single-reg single-float
(single-float -0s0 0s0))
(frob double-float-compare-zero double-reg double-float
(double-float -0d0 0d0)))
(macrolet ((frob (translate cond sname dname is-=)
`(progn
(define-vop (,sname single-float-compare-zero)
(:translate ,translate)
(:conditional ,cond)
(:variant :single ,is-=))
(define-vop (,dname double-float-compare-zero)
(:translate ,translate)
(:conditional ,cond)
(:variant :double ,is-=)))))
(frob < :mi </single-float-zero </double-float-zero nil)
(frob > :gt >/single-float-zero >/double-float-zero nil)
(frob = :eq eql/single-float-zero eql/double-float-zero t))
;;;; Conversion:
(macrolet ((frob (name translate inst from-sc from-type to-sc to-type)
`(define-vop (,name)
(:args (x :scs (,from-sc)))
(:temporary (:scs (single-reg)) rtemp)
(:results (y :scs (,to-sc)))
(:arg-types ,from-type)
(:result-types ,to-type)
(:policy :fast-safe)
(:note "inline float coercion")
(:translate ,translate)
(:vop-var vop)
(:save-p :compute-only)
(:generator 5
(inst fmsr rtemp x)
(inst ,inst y rtemp)))))
(frob %single-float/signed %single-float fsitos
signed-reg signed-num single-reg single-float)
(frob %double-float/signed %double-float fsitod
signed-reg signed-num double-reg double-float)
(frob %single-float/unsigned %single-float fuitos
unsigned-reg unsigned-num single-reg single-float)
(frob %double-float/unsigned %double-float fuitod
unsigned-reg unsigned-num double-reg double-float))
(macrolet ((frob (name translate inst from-sc from-type to-sc to-type)
`(define-vop (,name)
(:args (x :scs (,from-sc)))
(:results (y :scs (,to-sc)))
(:arg-types ,from-type)
(:result-types ,to-type)
(:policy :fast-safe)
(:note "inline float coercion")
(:translate ,translate)
(:vop-var vop)
(:save-p :compute-only)
(:generator 2
(note-this-location vop :internal-error)
(inst ,inst y x)))))
(frob %single-float/double-float %single-float fcvtsd
double-reg double-float single-reg single-float)
(frob %double-float/single-float %double-float fcvtds
single-reg single-float double-reg double-float))
(macrolet ((frob (trans from-sc from-type inst)
`(define-vop (,(symbolicate trans "/" from-type))
(:args (x :scs (,from-sc) :target temp))
(:temporary (:from (:argument 0) :sc single-reg) temp)
(:results (y :scs (signed-reg)))
(:arg-types ,from-type)
(:result-types signed-num)
(:translate ,trans)
(:policy :fast-safe)
(:note "inline float truncate")
(:vop-var vop)
(:save-p :compute-only)
(:generator 5
(note-this-location vop :internal-error)
(inst ,inst temp x)
(inst fmrs y temp)))))
(frob %unary-truncate/single-float single-reg single-float ftosizs)
(frob %unary-truncate/double-float double-reg double-float ftosizd)
(frob %unary-round single-reg single-float ftosis)
(frob %unary-round double-reg double-float ftosid))
(define-vop (make-single-float)
(:args (bits :scs (signed-reg) :target res
:load-if (not (sc-is bits signed-stack))))
(:results (res :scs (single-reg)
:load-if (not (sc-is res single-stack))))
(:temporary (:scs (signed-reg) :from (:argument 0) :to (:result 0)) temp)
(:arg-types signed-num)
(:result-types single-float)
(:translate make-single-float)
(:policy :fast-safe)
(:vop-var vop)
(:generator 4
(sc-case bits
(signed-reg
(sc-case res
(single-reg
(inst fmsr res bits))
(single-stack
(storew bits (current-nfp-tn vop) (tn-offset res)))))
(signed-stack
(sc-case res
(single-reg
(inst flds res
(@ (current-nfp-tn vop)
(* (tn-offset bits) n-word-bytes))))
(single-stack
(unless (location= bits res)
(loadw temp (current-nfp-tn vop) (tn-offset bits))
(storew temp (current-nfp-tn vop) (tn-offset res)))))))))
(define-vop (make-double-float)
(:args (hi-bits :scs (signed-reg))
(lo-bits :scs (unsigned-reg)))
(:results (res :scs (double-reg)
:load-if (not (sc-is res double-stack))))
(:arg-types signed-num unsigned-num)
(:result-types double-float)
(:translate make-double-float)
(:policy :fast-safe)
(:vop-var vop)
(:generator 2
(sc-case res
(double-reg
(inst fmdrr res lo-bits hi-bits))
(double-stack
(cond
((eq *backend-byte-order* :big-endian)
(storew hi-bits (current-nfp-tn vop) (tn-offset res))
(storew lo-bits (current-nfp-tn vop) (1+ (tn-offset res))))
(t
(storew lo-bits (current-nfp-tn vop) (tn-offset res))
(storew hi-bits (current-nfp-tn vop) (1+ (tn-offset res)))))))))
(define-vop (single-float-bits)
(:args (float :scs (single-reg descriptor-reg)
:load-if (not (sc-is float single-stack))))
(:results (bits :scs (signed-reg)
:load-if (or (sc-is float descriptor-reg single-stack)
(not (sc-is bits signed-stack)))))
(:arg-types single-float)
(:result-types signed-num)
(:translate single-float-bits)
(:policy :fast-safe)
(:vop-var vop)
(:generator 4
(sc-case bits
(signed-reg
(sc-case float
(single-reg
(inst fmrs bits float))
(single-stack
(loadw bits (current-nfp-tn vop) (tn-offset float)))
(descriptor-reg
(loadw bits float single-float-value-slot other-pointer-lowtag))))
(signed-stack
(sc-case float
(single-reg
(inst fsts float (@ (current-nfp-tn vop)
(* (tn-offset bits) n-word-bytes))))
((single-stack descriptor-reg)
;; Fun and games: This also affects PPC, silently.
;; Hopefully it's a non-issue, but I'd rather have the
;; explicit error than a silent miscompilation.
(bug "Unable to extract single-float bits from ~S to ~S" float bits)))))))
(define-vop (double-float-high-bits)
(:args (float :scs (double-reg descriptor-reg)
:load-if (not (sc-is float double-stack))))
(:results (hi-bits :scs (signed-reg)))
(:arg-types double-float)
(:result-types signed-num)
(:translate double-float-high-bits)
(:policy :fast-safe)
(:vop-var vop)
(:generator 5
(sc-case float
(double-reg
(inst fmrdh hi-bits float))
(double-stack
(loadw hi-bits (current-nfp-tn vop)
(+ (tn-offset float)
(if (eq *backend-byte-order* :big-endian)
0 1))))
(descriptor-reg
(loadw hi-bits float (+ double-float-value-slot
(if (eq *backend-byte-order* :big-endian)
0 1))
other-pointer-lowtag)))))
(define-vop (double-float-low-bits)
(:args (float :scs (double-reg descriptor-reg)
:load-if (not (sc-is float double-stack))))
(:results (lo-bits :scs (unsigned-reg)))
(:arg-types double-float)
(:result-types unsigned-num)
(:translate double-float-low-bits)
(:policy :fast-safe)
(:vop-var vop)
(:generator 5
(sc-case float
(double-reg
(inst fmrdl lo-bits float))
(double-stack
(loadw lo-bits (current-nfp-tn vop)
(+ (tn-offset float)
(if (eq *backend-byte-order* :big-endian)
1 0))))
(descriptor-reg
(loadw lo-bits float (+ double-float-value-slot
(if (eq *backend-byte-order* :big-endian)
1 0))
other-pointer-lowtag)))))
;;;; Float mode hackery:
(sb!xc:deftype float-modes () '(unsigned-byte 32))
(defknown floating-point-modes () float-modes (flushable))
(defknown ((setf floating-point-modes)) (float-modes)
float-modes)
(define-vop (floating-point-modes)
(:results (res :scs (unsigned-reg)))
(:result-types unsigned-num)
(:translate floating-point-modes)
(:policy :fast-safe)
(:generator 3
(inst fmrx res :fpscr)))
(define-vop (set-floating-point-modes)
(:args (new :scs (unsigned-reg) :target res))
(:results (res :scs (unsigned-reg)))
(:arg-types unsigned-num)
(:result-types unsigned-num)
(:translate (setf floating-point-modes))
(:policy :fast-safe)
(:generator 3
(inst fmxr :fpscr new)
(move res new)))
;;;; Complex float VOPs
(define-vop (make-complex-single-float)
(:translate complex)
(:args (real :scs (single-reg) :target r
:load-if (not (location= real r)))
(imag :scs (single-reg) :to :save))
(:arg-types single-float single-float)
(:results (r :scs (complex-single-reg) :from (:argument 0)
:load-if (not (sc-is r complex-single-stack))))
(:result-types complex-single-float)
(:note "inline complex single-float creation")
(:policy :fast-safe)
(:vop-var vop)
(:generator 5
(sc-case r
(complex-single-reg
(let ((r-real (complex-single-reg-real-tn r)))
(move-single r-real real))
(let ((r-imag (complex-single-reg-imag-tn r)))
(move-single r-imag imag)))
(complex-single-stack
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset r) n-word-bytes)))
(unless (location= real r)
(inst fsts real (@ nfp offset)))
(inst fsts imag (@ nfp (+ offset n-word-bytes))))))))
(define-vop (make-complex-double-float)
(:translate complex)
(:args (real :scs (double-reg) :target r
:load-if (not (location= real r)))
(imag :scs (double-reg) :to :save))
(:arg-types double-float double-float)
(:results (r :scs (complex-double-reg) :from (:argument 0)
:load-if (not (sc-is r complex-double-stack))))
(:result-types complex-double-float)
(:note "inline complex double-float creation")
(:policy :fast-safe)
(:vop-var vop)
(:generator 5
(sc-case r
(complex-double-reg
(let ((r-real (complex-double-reg-real-tn r)))
(move-double r-real real))
(let ((r-imag (complex-double-reg-imag-tn r)))
(move-double r-imag imag)))
(complex-double-stack
(let ((nfp (current-nfp-tn vop))
(offset (* (tn-offset r) n-word-bytes)))
(unless (location= real r)
(inst fstd real (@ nfp offset)))
(inst fstd imag (@ nfp (+ offset (* 2 n-word-bytes)))))))))
(define-vop (complex-single-float-value)
(:args (x :scs (complex-single-reg) :target r
:load-if (not (sc-is x complex-single-stack))))
(:arg-types complex-single-float)
(:results (r :scs (single-reg)))
(:result-types single-float)
(:variant-vars slot)
(:policy :fast-safe)
(:vop-var vop)
(:generator 3
(sc-case x
(complex-single-reg
(let ((value-tn (ecase slot
(:real (complex-single-reg-real-tn x))
(:imag (complex-single-reg-imag-tn x)))))
(move-single r value-tn)))
(complex-single-stack
(inst flds r (@ (current-nfp-tn vop)
(* (+ (ecase slot (:real 0) (:imag 1))
(tn-offset x))
n-word-bytes)))))))
(define-vop (realpart/complex-single-float complex-single-float-value)
(:translate realpart)
(:note "complex single float realpart")
(:variant :real))
(define-vop (imagpart/complex-single-float complex-single-float-value)
(:translate imagpart)
(:note "complex single float imagpart")
(:variant :imag))
(define-vop (complex-double-float-value)
(:args (x :scs (complex-double-reg) :target r
:load-if (not (sc-is x complex-double-stack))))
(:arg-types complex-double-float)
(:results (r :scs (double-reg)))
(:result-types double-float)
(:variant-vars slot)
(:policy :fast-safe)
(:vop-var vop)
(:generator 3
(sc-case x
(complex-double-reg
(let ((value-tn (ecase slot
(:real (complex-double-reg-real-tn x))
(:imag (complex-double-reg-imag-tn x)))))
(move-double r value-tn)))
(complex-double-stack
(inst fldd r (@ (current-nfp-tn vop)
(* (+ (ecase slot (:real 0) (:imag 2))
(tn-offset x))
n-word-bytes)))))))
(define-vop (realpart/complex-double-float complex-double-float-value)
(:translate realpart)
(:note "complex double float realpart")
(:variant :real))
(define-vop (imagpart/complex-double-float complex-double-float-value)
(:translate imagpart)
(:note "complex double float imagpart")
(:variant :imag))

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,452 @@
;;;; a bunch of handy macros for the ARM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;; Instruction-like macros.
(defmacro move (dst src &optional (predicate :al))
"Move SRC into DST unless they are location=."
(once-only ((n-dst dst)
(n-src src))
`(unless (location= ,n-dst ,n-src)
(inst mov ,predicate ,n-dst ,n-src))))
(macrolet
((def (type inst)
(let ((real-tn-fn (symbolicate 'complex- type '-reg-real-tn))
(imag-tn-fn (symbolicate 'complex- type '-reg-imag-tn)))
`(progn
(defmacro ,(symbolicate 'move- type)
(dst src &optional (predicate :al))
(once-only ((n-dst dst)
(n-src src))
`(unless (location= ,n-dst ,n-src)
(inst ,',inst ,predicate ,n-dst ,n-src))))
(defmacro ,(symbolicate 'move-complex- type)
(dst src &optional (predicate :al))
(once-only ((n-dst dst)
(n-src src))
`(unless (location= ,n-dst ,n-src)
;; Note that the complex (single and double) float
;; registers are aligned to paired underlying
;; (single and double) registers, so there is no
;; need to worry about overlap.
(let ((src-real (,',real-tn-fn ,n-src))
(dst-real (,',real-tn-fn ,n-dst)))
(inst ,',inst ,predicate dst-real src-real))
(let ((src-imag (,',imag-tn-fn ,n-src))
(dst-imag (,',imag-tn-fn ,n-dst)))
(inst ,', inst ,predicate dst-imag src-imag)))))))))
(def single fcpys)
(def double fcpyd))
(macrolet
((def (op inst shift)
`(defmacro ,op (object base
&optional (offset 0) (lowtag 0) (predicate :al))
`(inst ,',inst ,predicate ,object
(@ ,base (- (ash ,offset ,,shift) ,lowtag))))))
(def loadw ldr word-shift)
(def storew str word-shift))
(defmacro load-symbol (reg symbol)
(once-only ((reg reg) (symbol symbol))
`(progn
(composite-immediate-instruction add ,reg null-tn (static-symbol-offset ,symbol)))))
(defmacro load-symbol-value (reg symbol &optional (predicate :al))
`(inst ldr ,predicate ,reg
(@ null-tn
(+ (static-symbol-offset ',symbol)
(ash symbol-value-slot word-shift)
(- other-pointer-lowtag)))))
(defmacro store-symbol-value (reg symbol &optional (predicate :al))
`(inst str ,predicate ,reg
(@ null-tn
(+ (static-symbol-offset ',symbol)
(ash symbol-value-slot word-shift)
(- other-pointer-lowtag)))))
(defmacro load-type (target source &optional (offset 0) (predicate :al))
"Loads the type bits of a pointer into target independent of
byte-ordering issues."
(once-only ((n-target target)
(n-source source)
(n-offset offset))
(let ((target-offset (ecase *backend-byte-order*
(:little-endian n-offset)
(:big-endian `(+ ,n-offset (1- n-word-bytes))))))
`(inst ldrb ,predicate ,n-target (@ ,n-source ,target-offset)))))
;;; Macros to handle the fact that our stack pointer isn't actually in
;;; a register (or won't be, by the time we're done).
(defmacro load-csp (target &optional (predicate :al))
`(load-symbol-value ,target *control-stack-pointer* ,predicate))
(defmacro store-csp (source &optional (predicate :al))
`(store-symbol-value ,source *control-stack-pointer* ,predicate))
;;; Macros to handle the fact that we cannot use the machine native call and
;;; return instructions.
(defmacro lisp-jump (function)
"Jump to the lisp function FUNCTION."
`(inst add pc-tn ,function
(- (ash simple-fun-code-offset word-shift)
fun-pointer-lowtag)))
(defmacro lisp-return (return-pc return-style)
"Return to RETURN-PC."
`(progn
;; Indicate a single-valued return by clearing all of the status
;; flags, or a multiple-valued return by setting all of the status
;; flags.
,(ecase return-style
(:single-value '(inst msr (cpsr :f) 0))
(:multiple-values '(inst msr (cpsr :f) #xf0000000))
(:known))
#+(or) ;; Doesn't work, can't have a negative immediate value.
(inst add pc-tn ,return-pc (- 4 other-pointer-lowtag))
(inst sub pc-tn ,return-pc (- other-pointer-lowtag 4))))
(defmacro emit-return-pc (label)
"Emit a return-pc header word. LABEL is the label to use for this return-pc."
`(progn
(emit-alignment n-lowtag-bits)
(emit-label ,label)
(inst lra-header-word)))
;;;; Stack TN's
;;; Move a stack TN to a register and vice-versa.
(defun load-stack-offset (reg stack stack-tn &optional (predicate :al))
(let ((offset (* (tn-offset stack-tn) n-word-bytes)))
(cond ((or (tn-p offset)
(typep offset '(unsigned-byte 12)))
(inst ldr predicate reg (@ stack offset)))
(t
(load-immediate-word reg offset)
(inst ldr predicate reg (@ stack reg))))))
(defmacro load-stack-tn (reg stack &optional (predicate :al))
`(let ((reg ,reg)
(stack ,stack))
(sc-case stack
((control-stack)
(load-stack-offset reg cfp-tn stack ,predicate)))))
(defun store-stack-offset (reg stack stack-tn &optional (predicate :al))
(let ((offset (* (tn-offset stack-tn) n-word-bytes)))
(cond ((or (typep offset '(unsigned-byte 12))
(tn-p offset))
(inst str predicate reg (@ stack offset)))
(t
(let ((low (ldb (byte 12 0) offset))
(high (mask-field (byte 20 12) offset)))
;; KLUDGE:
;; Have to do this because it is used in move vops
;; which do not have temporary registers.
;; The debugger will be not happy.
(composite-immediate-instruction add stack stack high)
(inst str predicate reg (@ stack low))
(composite-immediate-instruction sub stack stack high))))))
(defmacro store-stack-tn (stack reg &optional (predicate :al))
`(let ((stack ,stack)
(reg ,reg))
(sc-case stack
((control-stack)
(store-stack-offset reg cfp-tn stack ,predicate)))))
(defmacro maybe-load-stack-tn (reg reg-or-stack)
"Move the TN Reg-Or-Stack into Reg if it isn't already there."
(once-only ((n-reg reg)
(n-stack reg-or-stack))
`(sc-case ,n-reg
((any-reg descriptor-reg)
(sc-case ,n-stack
((any-reg descriptor-reg)
(move ,n-reg ,n-stack))
((control-stack)
(load-stack-offset ,n-reg cfp-tn ,n-stack)))))))
;;;; Storage allocation:
;;; This is the main mechanism for allocating memory in the lisp heap.
;;;
;;; The allocated space is stored in RESULT-TN with the lowtag LOWTAG
;;; applied. The amount of space to be allocated is SIZE bytes (which
;;; must be a multiple of the lisp object size).
;;;
;;; Each platform seems to have its own slightly different way to do
;;; heap allocation, taking various different options as parameters.
;;; For ARM, we take the bare minimum parameters, RESULT-TN, SIZE, and
;;; LOWTAG, and we require a single temporary register called FLAG-TN
;;; to emphasize the parallelism with PSEUDO-ATOMIC (which must
;;; surround a call to ALLOCATION anyway), and to indicate that the
;;; P-A FLAG-TN is also acceptable here.
#!+gencgc
(defun allocation-tramp (alloc-tn size back-label)
(let ((fixup (gen-label)))
(when (integerp size)
(load-immediate-word alloc-tn size))
(emit-word sb!assem::**current-segment** (logior #xe92d0000
(ash 1 (if (integerp size)
(tn-offset alloc-tn)
(tn-offset size)))
(ash 1 (tn-offset lr-tn))))
(inst load-from-label alloc-tn alloc-tn fixup)
(inst blx alloc-tn)
(emit-word sb!assem::**current-segment** (logior #xe8bd0000
(ash 1 (tn-offset alloc-tn))
(ash 1 (tn-offset lr-tn))))
(inst b back-label)
(emit-label fixup)
(inst word (make-fixup "alloc_tramp" :foreign))))
(defmacro allocation (result-tn size lowtag &key flag-tn
stack-allocate-p)
;; Normal allocation to the heap.
(once-only ((result-tn result-tn)
(size size)
(lowtag lowtag)
(flag-tn flag-tn)
(stack-allocate-p stack-allocate-p))
`(cond (,stack-allocate-p
(load-csp ,result-tn)
(inst tst ,result-tn lowtag-mask)
(inst add :ne ,result-tn ,result-tn n-word-bytes)
(if (integerp ,size)
(composite-immediate-instruction add ,flag-tn ,result-tn ,size)
(inst add ,flag-tn ,result-tn ,size))
(store-csp ,flag-tn)
;; :ne is from TST above, this needs to be done after the
;; stack pointer has been stored.
(storew null-tn ,result-tn -1 0 :ne)
(inst orr ,result-tn ,result-tn ,lowtag))
#!-gencgc
(t
(load-symbol-value ,flag-tn *allocation-pointer*)
(inst add ,result-tn ,flag-tn ,lowtag)
(if (integerp ,size)
(composite-immediate-instruction add ,flag-tn ,flag-tn ,size)
(inst add ,flag-tn ,flag-tn ,size))
(store-symbol-value ,flag-tn *allocation-pointer*))
#!+gencgc
(t
(let ((fixup (gen-label))
(alloc (gen-label))
(back-from-alloc (gen-label)))
(inst load-from-label ,flag-tn ,flag-tn FIXUP)
(loadw ,result-tn ,flag-tn)
(loadw ,flag-tn ,flag-tn 1)
(if (integerp ,size)
(composite-immediate-instruction add ,result-tn ,result-tn ,size)
(inst add ,result-tn ,result-tn ,size))
(inst cmp ,result-tn ,flag-tn)
(inst b :hi ALLOC)
(inst load-from-label ,flag-tn ,flag-tn FIXUP)
(storew ,result-tn ,flag-tn)
(if (integerp ,size)
(composite-immediate-instruction sub ,result-tn ,result-tn ,size)
(inst sub ,result-tn ,result-tn ,size))
(emit-label BACK-FROM-ALLOC)
(when ,lowtag
(inst orr ,result-tn ,result-tn ,lowtag))
(assemble (*elsewhere*)
(emit-label ALLOC)
(allocation-tramp ,result-tn ,size BACK-FROM-ALLOC)
(emit-label FIXUP)
(inst word (make-fixup "boxed_region" :foreign))))))))
(defmacro with-fixed-allocation ((result-tn flag-tn type-code size
&key (lowtag other-pointer-lowtag)
stack-allocate-p)
&body body)
"Do stuff to allocate an other-pointer object of fixed Size with a single
word header having the specified Type-Code. The result is placed in
Result-TN, and Temp-TN is a non-descriptor temp (which may be randomly used
by the body.) The body is placed inside the PSEUDO-ATOMIC, and presumably
initializes the object."
(once-only ((result-tn result-tn) (flag-tn flag-tn)
(type-code type-code) (size size) (lowtag lowtag))
`(pseudo-atomic (,flag-tn)
(allocation ,result-tn (pad-data-block ,size) ,lowtag
:flag-tn ,flag-tn
:stack-allocate-p ,stack-allocate-p)
(when ,type-code
(inst mov ,flag-tn (ash (1- ,size) n-widetag-bits))
(inst orr ,flag-tn ,flag-tn ,type-code)
(storew ,flag-tn ,result-tn 0 ,lowtag))
,@body)))
;;;; Error Code
(defun emit-error-break (vop kind code values)
(assemble ()
(when vop
(note-this-location vop :internal-error))
;; Use the magic officially-undefined instruction that Linux
;; treats as generating SIGTRAP.
(inst debug-trap)
;; The rest of this is "just" the encoded error details.
(inst byte kind)
(with-adjustable-vector (vector)
(write-var-integer code vector)
(dolist (tn values)
(write-var-integer (make-sc-offset (sc-number (tn-sc tn))
(or (tn-offset tn) 0))
vector))
(inst byte (length vector))
(dotimes (i (length vector))
(inst byte (aref vector i)))
(emit-alignment word-shift))))
(defun error-call (vop error-code &rest values)
#!+sb-doc
"Cause an error. ERROR-CODE is the error to cause."
(emit-error-break vop error-trap (error-number-or-lose error-code) values))
(defun generate-error-code (vop error-code &rest values)
#!+sb-doc
"Generate-Error-Code Error-code Value*
Emit code for an error with the specified Error-Code and context Values."
(assemble (*elsewhere*)
(let ((start-lab (gen-label)))
(emit-label start-lab)
(emit-error-break vop error-trap (error-number-or-lose error-code) values)
start-lab)))
;;;; PSEUDO-ATOMIC
;;; handy macro for making sequences look atomic
;;; With LINK being NIL this doesn't store the next PC in LR when
;;; calling do_pending_interrupt.
;;; This used by allocate-vector-on-heap, there's a comment explaining
;;; why it needs that.
(defmacro pseudo-atomic ((flag-tn &key (link t)) &body forms)
`(progn
(without-scheduling ()
(store-symbol-value pc-tn *pseudo-atomic-atomic*))
(assemble ()
,@forms)
(without-scheduling ()
(store-symbol-value null-tn *pseudo-atomic-atomic*)
(load-symbol-value ,flag-tn *pseudo-atomic-interrupted*)
;; When *pseudo-atomic-interrupted* is not 0 it contains the address of
;; do_pending_interrupt
(inst cmp ,flag-tn 0)
,(if link
`(inst blx :ne ,flag-tn)
`(inst bx :ne ,flag-tn)))))
;;;; memory accessor vop generators
(defmacro define-full-reffer (name type offset lowtag scs el-type
&optional translate)
`(define-vop (,name)
,@(when translate
`((:translate ,translate)))
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg)))
(:arg-types ,type tagged-num)
(:temporary (:scs (interior-reg)) lip)
(:results (value :scs ,scs))
(:result-types ,el-type)
(:generator 5
(inst add lip object index)
(loadw value lip ,offset ,lowtag))))
(defmacro define-full-setter (name type offset lowtag scs el-type
&optional translate)
`(define-vop (,name)
,@(when translate
`((:translate ,translate)))
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (any-reg))
(value :scs ,scs :target result))
(:arg-types ,type tagged-num ,el-type)
(:temporary (:scs (interior-reg)) lip)
(:results (result :scs ,scs))
(:result-types ,el-type)
(:generator 2
(inst add lip object index)
(storew value lip ,offset ,lowtag)
(move result value))))
(defmacro define-partial-reffer (name type size signed offset lowtag scs
el-type &optional translate)
`(define-vop (,name)
,@(when translate
`((:translate ,translate)))
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (unsigned-reg)))
(:arg-types ,type positive-fixnum)
(:results (value :scs ,scs))
(:result-types ,el-type)
(:temporary (:scs (interior-reg)) lip)
(:generator 5
,(if (eq size :byte)
'(inst add lip object index)
'(inst add lip object (lsl index 1)))
(inst ,(ecase size
(:byte (if signed 'ldrsb 'ldrb))
(:short (if signed 'ldrsh 'ldrh)))
value (@ lip (- (* ,offset n-word-bytes) ,lowtag))))))
(defmacro define-partial-setter (name type size offset lowtag scs el-type
&optional translate)
`(define-vop (,name)
,@(when translate
`((:translate ,translate)))
(:policy :fast-safe)
(:args (object :scs (descriptor-reg))
(index :scs (unsigned-reg))
(value :scs ,scs :target result))
(:arg-types ,type positive-fixnum ,el-type)
(:temporary (:scs (interior-reg)) lip)
(:results (result :scs ,scs))
(:result-types ,el-type)
(:generator 5
,(if (eq size :byte)
'(inst add lip object index)
'(inst add lip object (lsl index 1)))
(inst ,(ecase size (:byte 'strb) (:short 'strh))
value (@ lip (- (* ,offset n-word-bytes) ,lowtag)))
(move result value))))
(def!macro with-pinned-objects ((&rest objects) &body body)
"Arrange with the garbage collector that the pages occupied by
OBJECTS will not be moved in memory for the duration of BODY.
Useful for e.g. foreign calls where another thread may trigger
garbage collection. This is currently implemented by disabling GC"
#!-gencgc
(declare (ignore objects)) ; should we eval these for side-effect?
#!-gencgc
`(without-gcing
,@body)
#!+gencgc
`(let ((*pinned-objects* (list* ,@objects *pinned-objects*)))
(declare (truly-dynamic-extent *pinned-objects*))
,@body))

View file

@ -0,0 +1,55 @@
;;;; the ARM definitions of some general purpose memory reference VOPs
;;;; inherited by basic memory reference operations
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;; Cell-Ref and Cell-Set are used to define VOPs like CAR, where the
;;; offset to be read or written is a property of the VOP used.
(define-vop (cell-ref)
(:args (object :scs (descriptor-reg)))
(:results (value :scs (descriptor-reg any-reg)))
(:variant-vars offset lowtag)
(:policy :fast-safe)
(:generator 4
(loadw value object offset lowtag)))
(define-vop (cell-set)
(:args (object :scs (descriptor-reg))
(value :scs (descriptor-reg any-reg null)))
(:variant-vars offset lowtag)
(:policy :fast-safe)
(:generator 4
(storew value object offset lowtag)))
;;; Slot-Ref and Slot-Set are used to define VOPs like Closure-Ref,
;;; where the offset is constant at compile time, but varies for
;;; different uses.
;;; The PPC backend says "We add in the standard g-vector overhead",
;;; what does this mean? -- AB 2012-Oct-27
(define-vop (slot-ref)
(:args (object :scs (descriptor-reg)))
(:results (value :scs (descriptor-reg any-reg)))
(:variant-vars base lowtag)
(:info offset)
(:generator 4
(loadw value object (+ base offset) lowtag)))
(define-vop (slot-set)
(:args (object :scs (descriptor-reg))
(value :scs (descriptor-reg any-reg)))
(:variant-vars base lowtag)
(:info offset)
(:generator 4
(storew value object (+ base offset) lowtag)))

View file

@ -0,0 +1,325 @@
;;;; the ARM VM definition of operand loading/saving and the Move VOP
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(defun lowest-set-bit-index (integer-value)
(max 0 (1- (integer-length (logand integer-value (- integer-value))))))
(defun repeating-pattern-p (val)
(declare (type (unsigned-byte 32) val))
(and (= (ldb (byte 16 0) val)
(ldb (byte 16 16) val))
(not (encodable-immediate (ldb (byte 16 16) val)))))
;;; This should be put into composite-immediate-instruction, but that
;;; macro is too scary.
(defun load-repeating-pattern (dest val)
(declare (type (signed-byte 32) val))
(inst mov dest (ldb (byte 8 0) val))
(inst orr dest dest (mask-field (byte 8 8) val))
(inst orr dest dest (lsl dest 16)))
(defun load-immediate-word (y val)
(cond ((let ((unsigned (ldb (byte 32 0) val)))
(when (encodable-immediate unsigned)
(inst mov y unsigned)
t)))
((let ((inverted (ldb (byte 32 0) (lognot val))))
(when (encodable-immediate inverted)
(inst mvn y inverted)
t)))
((< val 0)
(composite-immediate-instruction bic y y val :first-op mvn :first-no-source t :invert-y t))
((repeating-pattern-p val)
(load-repeating-pattern y val))
(t
(composite-immediate-instruction orr y y val :first-op mov :first-no-source t))))
(define-move-fun (load-immediate 1) (vop x y)
((null immediate)
(any-reg descriptor-reg))
(let ((val (tn-value x)))
(etypecase val
(integer
;; This is a FIXNUM, as IMMEDIATE-CONSTANT-SC only
;; accepts integers if they are FIXNUMs.
(load-immediate-word y (fixnumize val)))
(character
(let* ((codepoint (char-code val))
(encoded-character (dpb codepoint (byte 24 8) character-widetag)))
(load-immediate-word y encoded-character)))
(null
(move y null-tn))
(symbol
(load-symbol y val)))))
(define-move-fun (load-number 1) (vop x y)
((immediate)
(signed-reg unsigned-reg))
(load-immediate-word y (tn-value x)))
(define-move-fun (load-character 1) (vop x y)
((immediate) (character-reg))
(load-immediate-word y (char-code (tn-value x))))
(define-move-fun (load-system-area-pointer 1) (vop x y)
((immediate) (sap-reg))
(let ((immediate-label (gen-label)))
(assemble (*elsewhere*)
(emit-label immediate-label)
(inst word (sap-int (tn-value x))))
(inst ldr y (@ immediate-label))))
(define-move-fun (load-constant 5) (vop x y)
((constant) (descriptor-reg))
(let ((offset (- (ash (tn-offset x) 2) other-pointer-lowtag)))
(typecase offset
((unsigned-byte 12)
(inst ldr y (@ code-tn offset)))
(t
;; Y is a descriptor-reg, make sure offset is a fixnum.
(load-immediate-word y (ash offset n-fixnum-tag-bits))
(inst ldr y (@ code-tn (lsr y n-fixnum-tag-bits)))))))
(define-move-fun (load-stack 5) (vop x y)
((control-stack) (any-reg descriptor-reg))
(load-stack-tn y x))
(define-move-fun (load-number-stack 5) (vop x y)
((character-stack) (character-reg)
(sap-stack) (sap-reg)
(signed-stack) (signed-reg)
(unsigned-stack) (unsigned-reg))
(load-stack-offset y (current-nfp-tn vop) x))
(define-move-fun (store-stack 5) (vop x y)
((any-reg descriptor-reg) (control-stack))
(store-stack-tn y x))
(define-move-fun (store-number-stack 5) (vop x y)
((character-reg) (character-stack)
(sap-reg) (sap-stack)
(signed-reg) (signed-stack)
(unsigned-reg) (unsigned-stack))
(store-stack-offset x (current-nfp-tn vop) y))
;;;; The Move VOP:
(define-vop (move)
(:args (x :target y
:scs (any-reg descriptor-reg null)
:load-if (not (location= x y))))
(:results (y :scs (any-reg descriptor-reg)
:load-if (not (location= x y))))
(:effects)
(:affected)
(:generator 0
(move y x)))
(define-move-vop move :move
(any-reg descriptor-reg)
(any-reg descriptor-reg))
;;; Make MOVE the check VOP for T so that type check generation
;;; doesn't think it is a hairy type. This also allows checking of a
;;; few of the values in a continuation to fall out.
(primitive-type-vop move (:check) t)
;;; The MOVE-ARG VOP is used for moving descriptor values into another
;;; frame for argument or known value passing.
(define-vop (move-arg)
(:args (x :target y
:scs (any-reg descriptor-reg null))
(fp :scs (any-reg)
:load-if (not (sc-is y any-reg descriptor-reg))))
(:results (y))
(:generator 0
(sc-case y
((any-reg descriptor-reg)
(move y x))
(control-stack
(store-stack-offset x fp y)))))
;;;
(define-move-vop move-arg :move-arg
(any-reg descriptor-reg)
(any-reg descriptor-reg))
;;;; ILLEGAL-MOVE
;;; This VOP exists just to begin the lifetime of a TN that couldn't
;;; be written legally due to a type error. An error is signalled
;;; before this VOP is so we don't need to do anything (not that there
;;; would be anything sensible to do anyway.)
(define-vop (illegal-move)
(:args (x) (type))
(:results (y))
(:ignore y)
(:vop-var vop)
(:save-p :compute-only)
(:generator 666
(error-call vop 'object-not-type-error x type)))
;;;; Moves and coercions:
;;; These MOVE-TO-WORD VOPs move a tagged integer to a raw full-word
;;; representation. Similarly, the MOVE-FROM-WORD VOPs converts a raw integer
;;; to a tagged bignum or fixnum.
;;; ARG is a fixnum, so just shift it. We need a type restriction because some
;;; possible arg SCs (control-stack) overlap with possible bignum arg SCs.
(define-vop (move-to-word/fixnum)
(:args (x :scs (any-reg descriptor-reg)))
(:results (y :scs (signed-reg unsigned-reg)))
(:arg-types tagged-num)
(:note "fixnum untagging")
(:generator 1
(inst mov y (asr x n-fixnum-tag-bits))))
(define-move-vop move-to-word/fixnum :move
(any-reg descriptor-reg) (signed-reg unsigned-reg))
;;; ARG is a non-immediate constant; load it.
(define-vop (move-to-word-c)
(:args (x :scs (constant)))
(:results (y :scs (signed-reg unsigned-reg)))
(:vop-var vop)
(:note "constant load")
(:generator 1
(cond ((sb!c::tn-leaf x)
(load-immediate-word y (tn-value x)))
(t
(load-constant vop x y)
(inst mov y (asr y n-fixnum-tag-bits))))))
(define-move-vop move-to-word-c :move
(constant) (signed-reg unsigned-reg))
;;; ARG is a fixnum or bignum; figure out which and load if necessary.
(define-vop (move-to-word/integer)
(:args (x :scs (descriptor-reg)))
(:results (y :scs (signed-reg unsigned-reg)))
(:note "integer to untagged word coercion")
(:generator 4
(inst tst x fixnum-tag-mask)
(sc-case y
(signed-reg
(inst mov :eq y (asr x n-fixnum-tag-bits)))
(unsigned-reg
(inst mov :eq y (lsr x n-fixnum-tag-bits))))
(loadw y x bignum-digits-offset other-pointer-lowtag :ne)))
(define-move-vop move-to-word/integer :move
(descriptor-reg) (signed-reg unsigned-reg))
;;; RESULT is a fixnum, so we can just shift. We need the result type
;;; restriction because of the control-stack ambiguity noted above.
(define-vop (move-from-word/fixnum)
(:args (x :scs (signed-reg unsigned-reg)))
(:results (y :scs (any-reg descriptor-reg)))
(:result-types tagged-num)
(:note "fixnum tagging")
(:generator 1
(inst mov y (lsl x n-fixnum-tag-bits))))
(define-move-vop move-from-word/fixnum :move
(signed-reg unsigned-reg) (any-reg descriptor-reg))
;;; RESULT may be a bignum, so we have to check. Use a worst-case
;;; cost to make sure people know they may be number consing.
(define-vop (move-from-signed)
(:args (arg :scs (signed-reg unsigned-reg) :target x))
(:results (y :scs (any-reg descriptor-reg)))
(:temporary (:scs (non-descriptor-reg) :from (:argument 0)) x)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:note "signed word to integer coercion")
(:generator 20
(move x arg)
(inst adds pa-flag x x)
(inst adds :vc y pa-flag pa-flag)
(inst b :vc DONE)
(with-fixed-allocation (y pa-flag bignum-widetag (1+ bignum-digits-offset))
(storew x y bignum-digits-offset other-pointer-lowtag))
DONE))
(define-move-vop move-from-signed :move
(signed-reg) (descriptor-reg))
;;; Check for fixnum, and possibly allocate one or two word bignum
;;; result. Use a worst-case cost to make sure people know they may
;;; be number consing.
(define-vop (move-from-unsigned)
(:args (arg :scs (signed-reg unsigned-reg) :target x))
(:results (y :scs (any-reg descriptor-reg)))
(:temporary (:scs (non-descriptor-reg) :from (:argument 0)) x)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:note "unsigned word to integer coercion")
(:generator 20
(move x arg)
(inst tst x (ash (1- (ash 1 (- n-word-bits
n-positive-fixnum-bits)))
n-positive-fixnum-bits))
(inst mov y (lsl x n-fixnum-tag-bits))
(inst b :eq DONE)
(with-fixed-allocation
(y pa-flag bignum-widetag (+ 2 bignum-digits-offset))
;; WITH-FIXED-ALLOCATION, when using a supplied type-code,
;; leaves PA-FLAG containing the computed header value. In our
;; case, configured for a 2-word bignum. If the sign bit in the
;; value we're boxing is CLEAR, we need to shrink the bignum by
;; one word, hence the following:
(inst orrs x x 0)
(inst sub :pl pa-flag pa-flag #x100)
(storew pa-flag y 0 other-pointer-lowtag :pl)
(storew x y bignum-digits-offset other-pointer-lowtag))
DONE))
(define-move-vop move-from-unsigned :move
(unsigned-reg) (descriptor-reg))
;;; Move untagged numbers.
(define-vop (word-move)
(:args (x :target y
:scs (signed-reg unsigned-reg)
:load-if (not (location= x y))))
(:results (y :scs (signed-reg unsigned-reg)
:load-if (not (location= x y))))
(:effects)
(:affected)
(:note "word integer move")
(:generator 0
(move y x)))
(define-move-vop word-move :move
(signed-reg unsigned-reg) (signed-reg unsigned-reg))
;;; Move untagged number arguments/return-values.
(define-vop (move-word-arg)
(:args (x :target y
:scs (signed-reg unsigned-reg))
(fp :scs (any-reg)
:load-if (not (sc-is y signed-reg unsigned-reg))))
(:results (y))
(:note "word integer argument move")
(:generator 0
(sc-case y
((signed-reg unsigned-reg)
(move y x))
((signed-stack unsigned-stack)
(store-stack-offset x fp y)))))
(define-move-vop move-word-arg :move-arg
(descriptor-reg any-reg signed-reg unsigned-reg) (signed-reg unsigned-reg))
;;; Use standard MOVE-ARG + coercion to move an untagged number to a
;;; descriptor passing location.
(define-move-vop move-arg :move-arg
(signed-reg unsigned-reg) (any-reg descriptor-reg))

286
src/compiler/arm64/nlx.lisp Normal file
View file

@ -0,0 +1,286 @@
;;;; the ARM definitions of VOPs used for non-local exit (throw,
;;;; lexical exit, etc.)
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;; Make an environment-live stack TN for saving the SP for NLX entry.
(defun make-nlx-sp-tn (env)
(physenv-live-tn
(make-representation-tn *fixnum-primitive-type* immediate-arg-scn)
env))
;;; Make a TN for the argument count passing location for a
;;; non-local entry.
(defun make-nlx-entry-arg-start-location ()
(make-wired-tn *fixnum-primitive-type* immediate-arg-scn r8-offset))
;;; Save and restore dynamic environment.
;;;
;;; These VOPs are used in the reentered function to restore the appropriate
;;; dynamic environment. Currently we only save the Current-Catch and binding
;;; stack pointer. We don't need to save/restore the current unwind-protect,
;;; since unwind-protects are implicitly processed during unwinding. If there
;;; were any additional stacks, then this would be the place to restore the top
;;; pointers.
(define-vop (save-dynamic-state)
(:results (catch :scs (descriptor-reg))
(nfp :scs (descriptor-reg))
(nsp :scs (descriptor-reg)))
(:vop-var vop)
(:generator 13
(load-symbol-value catch *current-catch-block*)
(let ((cur-nfp (current-nfp-tn vop)))
(when cur-nfp
(move nfp cur-nfp)))
(move nsp nsp-tn)))
(define-vop (restore-dynamic-state)
(:args (catch :scs (descriptor-reg))
(nfp :scs (descriptor-reg))
(nsp :scs (descriptor-reg)))
(:vop-var vop)
(:generator 10
(store-symbol-value catch *current-catch-block*)
(let ((cur-nfp (current-nfp-tn vop)))
(when cur-nfp
(move cur-nfp nfp)))
(move nsp-tn nsp)))
(define-vop (current-stack-pointer)
(:results (res :scs (any-reg descriptor-reg)))
(:generator 1
(load-csp res)))
(define-vop (current-binding-pointer)
(:results (res :scs (any-reg descriptor-reg)))
(:generator 1
(load-symbol-value res *binding-stack-pointer*)))
;;;; Unwind block hackery:
;;; Compute the address of the catch block from its TN, then store into the
;;; block the current Fp, Env, Unwind-Protect, and the entry PC.
;;;
(define-vop (make-unwind-block)
(:args (tn))
(:info entry-label)
(:results (block :scs (any-reg)))
(:temporary (:scs (descriptor-reg)) temp)
(:temporary (:scs (interior-reg)) lip)
(:generator 22
(composite-immediate-instruction add block cfp-tn
(* (tn-offset tn) n-word-bytes))
(load-symbol-value temp *current-unwind-protect-block*)
(storew temp block unwind-block-current-uwp-slot)
(storew cfp-tn block unwind-block-current-cont-slot)
(storew code-tn block unwind-block-current-code-slot)
(inst compute-lra temp lip entry-label)
(storew temp block catch-block-entry-pc-slot)))
;;; Like Make-Unwind-Block, except that we also store in the specified tag, and
;;; link the block into the Current-Catch list.
;;;
(define-vop (make-catch-block)
(:args (tn) (tag :scs (any-reg descriptor-reg)))
(:info entry-label)
(:results (block :scs (any-reg)))
(:temporary (:scs (descriptor-reg)) temp)
(:temporary (:scs (descriptor-reg) :target block :to (:result 0)) result)
(:temporary (:scs (interior-reg)) lip)
(:generator 44
(composite-immediate-instruction
add result cfp-tn (* (tn-offset tn) n-word-bytes))
(load-symbol-value temp *current-unwind-protect-block*)
(storew temp result catch-block-current-uwp-slot)
(storew cfp-tn result catch-block-current-cont-slot)
(storew code-tn result catch-block-current-code-slot)
(inst compute-lra temp lip entry-label)
(storew temp result catch-block-entry-pc-slot)
(storew tag result catch-block-tag-slot)
(load-symbol-value temp *current-catch-block*)
(storew temp result catch-block-previous-catch-slot)
(store-symbol-value result *current-catch-block*)
(move block result)))
;;; Just set the current unwind-protect to TN's address. This
;;; instantiates an unwind block as an unwind-protect.
(define-vop (set-unwind-protect)
(:args (tn))
(:temporary (:scs (descriptor-reg)) new-uwp)
(:generator 7
(composite-immediate-instruction
add new-uwp cfp-tn (* (tn-offset tn) n-word-bytes))
(store-symbol-value new-uwp *current-unwind-protect-block*)))
(define-vop (unlink-catch-block)
(:temporary (:scs (any-reg)) block)
(:policy :fast-safe)
(:translate %catch-breakup)
(:generator 17
(load-symbol-value block *current-catch-block*)
(loadw block block catch-block-previous-catch-slot)
(store-symbol-value block *current-catch-block*)))
(define-vop (unlink-unwind-protect)
(:temporary (:scs (any-reg)) block)
(:policy :fast-safe)
(:translate %unwind-protect-breakup)
(:generator 17
(load-symbol-value block *current-unwind-protect-block*)
(loadw block block unwind-block-current-uwp-slot)
(store-symbol-value block *current-unwind-protect-block*)))
;;;; NLX entry VOPs:
(define-vop (nlx-entry)
(:args (sp) ; Note: we can't list an sc-restriction, 'cause any load vops
; would be inserted before the LRA.
(start)
(count))
(:results (values :more t))
(:temporary (:scs (descriptor-reg)) move-temp)
(:info label nvals)
(:save-p :force-to-stack)
(:vop-var vop)
(:generator 30
(emit-return-pc label)
(note-this-location vop :non-local-entry)
(cond ((zerop nvals))
((= nvals 1)
(inst cmp count 0)
(move (tn-ref-tn values) null-tn :eq)
(loadw (tn-ref-tn values) start 0 0 :ne))
(t
(do ((i 0 (1+ i))
(tn-ref values (tn-ref-across tn-ref)))
((null tn-ref))
(let ((tn (tn-ref-tn tn-ref)))
(inst subs count count (fixnumize 1))
(sc-case tn
((descriptor-reg any-reg)
(loadw tn start i 0 :ge)
(move tn null-tn :lt))
(control-stack
(loadw move-temp start i 0 :ge)
(store-stack-tn tn move-temp :ge)
(store-stack-tn tn null-tn :lt)))))))
(load-stack-tn move-temp sp)
(store-csp move-temp)))
(define-vop (nlx-entry-multiple)
(:args (top :target result) (src) (count))
;; Again, no SC restrictions for the args, 'cause the loading would
;; happen before the entry label.
(:info label)
(:temporary (:scs (any-reg)) dst)
(:temporary (:scs (descriptor-reg)) temp)
(:results (result :scs (any-reg) :from (:argument 0))
(num :scs (any-reg) :from (:argument 0)))
(:save-p :force-to-stack)
(:vop-var vop)
(:generator 30
(emit-return-pc label)
(note-this-location vop :non-local-entry)
;; Setup results, and test for the zero value case.
(load-stack-tn result top)
(inst cmp count 0)
(inst mov num 0)
(inst b :eq DONE)
;; Compute dst as one slot down from result, because we inc the index
;; before we use it.
(inst sub dst result 4)
;; Copy stuff down the stack.
LOOP
(inst ldr temp (@ src num))
(inst add num num (fixnumize 1))
(inst cmp num count)
(inst str temp (@ dst num))
(inst b :ne LOOP)
;; Reset the CSP.
DONE
(inst add temp result num)
(store-csp temp)))
;;; This VOP is just to force the TNs used in the cleanup onto the stack.
;;;
(define-vop (uwp-entry)
(:info label)
(:save-p :force-to-stack)
(:results (block) (start) (count))
(:ignore block start count)
(:vop-var vop)
(:generator 0
(emit-return-pc label)
(note-this-location vop :non-local-entry)))
(define-vop (unwind-to-frame-and-call)
(:args (ofp :scs (descriptor-reg))
(uwp :scs (descriptor-reg))
(function :scs (descriptor-reg) :to :load :target saved-function))
(:arg-types system-area-pointer system-area-pointer t)
(:temporary (:sc unsigned-reg) temp)
(:temporary (:sc descriptor-reg :offset r8-offset) saved-function)
(:temporary (:sc unsigned-reg :offset r0-offset) block)
(:temporary (:sc descriptor-reg :offset lexenv-offset) lexenv)
(:temporary (:scs (interior-reg)) lip)
(:temporary (:sc descriptor-reg :offset nargs-offset) nargs)
(:vop-var vop)
(:generator 22
(let ((uwp-label (gen-label))
(entry-label (gen-label)))
;; Store the function into a non-stack location, since we'll be
;; unwinding the stack and destroying register contents before we
;; use it. It turns out that R8 is preserved as part of the
;; normal multiple-value handling of an unwind, so use that.
(move saved-function function)
;; Allocate space for magic UWP block.
(load-csp block)
(inst add temp block (* unwind-block-size n-word-bytes))
(store-csp temp)
;; Set up magic catch / UWP block.
(loadw temp uwp sap-pointer-slot other-pointer-lowtag)
(storew temp block unwind-block-current-uwp-slot)
(loadw temp ofp sap-pointer-slot other-pointer-lowtag)
(storew temp block unwind-block-current-cont-slot)
;; Don't need to save code at unwind-block-current-code-slot since
;; it's not going to be used and will be overwritten after the
;; function call
(inst compute-lra temp lip entry-label)
(storew temp block catch-block-entry-pc-slot)
;; Run any required UWPs.
(assemble (*elsewhere* vop)
(emit-label uwp-label)
(inst word (make-fixup 'unwind :assembly-routine)))
(inst load-from-label pc-tn lr-tn uwp-label)
(emit-label ENTRY-LABEL)
;; KLUDGE: either COMPUTE-LRA computes or UNWIND jumps one
;; instruction further.
(inst mov nargs 0)
(inst mov nargs 0)
(move lexenv saved-function)
(loadw saved-function lexenv closure-fun-slot fun-pointer-lowtag)
(lisp-jump saved-function))))

View file

@ -0,0 +1,180 @@
;;;; This file contains some parameterizations of various VM
;;;; attributes for the ARM. This file is separate from other stuff so
;;;; that it can be compiled and loaded earlier.
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;; number of bits per word where a word holds one lisp descriptor
(def!constant n-word-bits 32)
;;; the natural width of a machine word (as seen in e.g. register width,
;;; address space)
(def!constant n-machine-word-bits 32)
;;; number of bits per byte where a byte is the smallest addressable
;;; object
(def!constant n-byte-bits 8)
;;; Floating-point related constants, both format descriptions and FPU
;;; control register descriptions. These don't exactly match up with
;;; what the machine manuals say because the Common Lisp standard
;;; defines floating-point values somewhat differently than the IEEE
;;; standard does.
(def!constant float-sign-shift 31)
(def!constant single-float-bias 126)
(defconstant-eqx single-float-exponent-byte (byte 8 23) #'equalp)
(defconstant-eqx single-float-significand-byte (byte 23 0) #'equalp)
(def!constant single-float-normal-exponent-min 1)
(def!constant single-float-normal-exponent-max 254)
(def!constant single-float-hidden-bit (ash 1 23))
(def!constant single-float-trapping-nan-bit (ash 1 22))
(def!constant double-float-bias 1022)
(defconstant-eqx double-float-exponent-byte (byte 11 20) #'equalp)
(defconstant-eqx double-float-significand-byte (byte 20 0) #'equalp)
(def!constant double-float-normal-exponent-min 1)
(def!constant double-float-normal-exponent-max #x7FE)
(def!constant double-float-hidden-bit (ash 1 20))
(def!constant double-float-trapping-nan-bit (ash 1 19))
(def!constant single-float-digits
(+ (byte-size single-float-significand-byte) 1))
(def!constant double-float-digits
(+ (byte-size double-float-significand-byte) n-word-bits 1))
#!+arm-vfp
(progn
(def!constant float-invalid-trap-bit (ash 1 0))
(def!constant float-divide-by-zero-trap-bit (ash 1 1))
(def!constant float-overflow-trap-bit (ash 1 2))
(def!constant float-underflow-trap-bit (ash 1 3))
(def!constant float-inexact-trap-bit (ash 1 4))
(def!constant float-input-denormal-trap-bit (ash 1 7))
(def!constant float-round-to-nearest 0)
(def!constant float-round-to-positive 1)
(def!constant float-round-to-negative 2)
(def!constant float-round-to-zero 3)
(defconstant-eqx float-rounding-mode (byte 2 22) #'equalp)
(defconstant-eqx float-sticky-bits (byte 8 0) #'equalp)
(defconstant-eqx float-traps-byte (byte 8 8) #'equalp)
(defconstant-eqx float-exceptions-byte (byte 8 0) #'equalp)
(def!constant float-fast-bit (ash 1 24))) ;; Flush-to-zero mode
;; NOTE: As with the FLOAT-REGISTERS SB in vm.lisp, if you define this
;; for non-VFP systems, please use a specific positive feature
;; conditional.
#!-arm-vfp
(error "Don't know how to set the FPU control word layout on non-VFP systems")
;;;; Where to put the different spaces.
;;; On non-gencgc we need large dynamic and static spaces for PURIFY
#!-gencgc
(progn
(def!constant read-only-space-start #x04000000)
(def!constant read-only-space-end #x07ff8000)
(def!constant static-space-start #x08000000)
(def!constant static-space-end #x097fff00)
(def!constant linkage-table-space-start #x0a000000)
(def!constant linkage-table-space-end #x0b000000))
#!+gencgc
(progn
(def!constant linkage-table-space-start #x0a000000)
(def!constant linkage-table-space-end #x0b000000)
(def!constant read-only-space-start #x04000000)
(def!constant read-only-space-end #x07ff8000)
(def!constant static-space-start #x08000000)
(def!constant static-space-end #x097fff00)
(def!constant dynamic-space-start #x4f000000)
(def!constant dynamic-space-end (!configure-dynamic-space-end)))
(def!constant linkage-table-entry-size 16)
#!+linux
(progn
#!-gencgc
(progn
(def!constant dynamic-0-space-start #x4f000000)
(def!constant dynamic-0-space-end #x66fff000)
(def!constant dynamic-1-space-start #x67000000)
(def!constant dynamic-1-space-end #x7efff000)))
;;;; other miscellaneous constants
(defenum (:start 8)
halt-trap
pending-interrupt-trap
error-trap
cerror-trap
breakpoint-trap
fun-end-breakpoint-trap
single-step-around-trap
single-step-before-trap)
;;;; Static symbols.
;;; These symbols are loaded into static space directly after NIL so
;;; that the system can compute their address by adding a constant
;;; amount to NIL.
;;;
;;; The fdefn objects for the static functions are loaded into static
;;; space directly after the static symbols. That way, the raw-addr
;;; can be loaded directly out of them by indirecting relative to NIL.
;;;
(defparameter *static-symbols*
(append
*common-static-symbols*
*c-callable-static-symbols*
'(*allocation-pointer*
*control-stack-pointer*
*binding-stack-pointer*
*interrupted-control-stack-pointer*
;; interrupt handling
*pseudo-atomic-atomic*
*pseudo-atomic-interrupted*
;; Needed for callbacks to work across saving cores. see
;; ALIEN-CALLBACK-ASSEMBLER-WRAPPER in c-call.lisp for gory
;; details.
sb!alien::*enter-alien-callback*
#!+gencgc *restart-lisp-function*)))
(defparameter *static-funs*
'(two-arg-gcd two-arg-lcm
two-arg-+ two-arg-- two-arg-* two-arg-/
two-arg-< two-arg-> two-arg-=
two-arg-and two-arg-ior two-arg-xor two-arg-eqv
eql
sb!kernel:%negate))
;;;; Assembler parameters:
;;; The number of bits per element in the assemblers code vector.
;;;
(defparameter *assembly-unit-length* 8)
(defvar *allocation-pointer*)

View file

@ -0,0 +1,69 @@
;;;; predicate VOPs for the ARM VM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; The Branch VOP.
;;; The unconditional branch, emitted when we can't drop through to the desired
;;; destination. Dest is the continuation we transfer control to.
;;;
(define-vop (branch)
(:info dest)
(:generator 5
(inst b dest)))
;;;; Generic conditional VOPs
;;; The generic conditional branch, emitted immediately after test
;;; VOPs that only set flags.
;;; FIXME: Unlike the PPC (from whence this was cribbed), ARM actually
;;; has flags. We should take advantage of them here.
(define-vop (branch-if)
(:info dest flags not-p)
(:generator 0
(flet ((negate-condition (name)
(let ((code (logxor 1 (conditional-opcode name))))
(aref *condition-name-vec* code))))
(aver (null (rest flags)))
(inst b
(if not-p
(negate-condition (first flags))
(first flags))
dest))))
(defun convert-conditional-move-p (node dst-tn x-tn y-tn)
(declare (ignore node dst-tn x-tn y-tn))
nil)
;;;; Conditional VOPs:
(define-vop (if-eq)
(:args (x :scs (any-reg descriptor-reg null))
(y :scs (any-reg descriptor-reg null)))
(:conditional)
(:info target not-p)
(:policy :fast-safe)
(:translate eq)
(:generator 3
(inst cmp
(sc-case x
(null null-tn) ;; FIXME: should it really be like that?
(t x))
(sc-case y
(null null-tn)
(t y)))
(inst b (if not-p :ne :eq) target)))

View file

@ -0,0 +1,29 @@
;;;; Do whatever is necessary to make the given code component
;;;; executable.
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; While most of SBCL is derived from the CMU CL system, the test
;;;; files (like this one) were written from scratch after the fork
;;;; from CMU CL.
;;;;
;;;; This software is in the public domain and is provided with
;;;; absolutely no warranty. See the COPYING and CREDITS files for
;;;; more information.
(in-package "SB!VM")
;;; Do whatever is necessary to make the given code component
;;; executable. This isn't always strictly necessary (some ARM
;;; systems have coherent caches, for example), but it covers the
;;; general case.
(defun sanctify-for-execution (component)
(without-gcing
(alien-funcall (extern-alien "os_flush_icache"
(function void
system-area-pointer
unsigned-long))
(code-instructions component)
(%code-code-size component)))
nil)

285
src/compiler/arm64/sap.lisp Normal file
View file

@ -0,0 +1,285 @@
;;;; the ARM VM definition of SAP operations
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; Moves and coercions:
;;; Move a tagged SAP to an untagged representation.
(define-vop (move-to-sap)
(:args (x :scs (any-reg descriptor-reg)))
(:results (y :scs (sap-reg)))
(:note "pointer to SAP coercion")
(:generator 1
(loadw y x sap-pointer-slot other-pointer-lowtag)))
(define-move-vop move-to-sap :move
(descriptor-reg) (sap-reg))
;;; Move an untagged SAP to a tagged representation.
(define-vop (move-from-sap)
(:args (sap :scs (sap-reg) :to :save))
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) pa-flag)
(:results (res :scs (descriptor-reg)))
(:note "SAP to pointer coercion")
(:generator 20
(with-fixed-allocation (res pa-flag sap-widetag sap-size)
(storew sap res sap-pointer-slot other-pointer-lowtag))))
(define-move-vop move-from-sap :move
(sap-reg) (descriptor-reg))
;;; Move untagged sap values.
(define-vop (sap-move)
(:args (x :target y
:scs (sap-reg)
:load-if (not (location= x y))))
(:results (y :scs (sap-reg)
:load-if (not (location= x y))))
(:note "SAP move")
(:effects)
(:affected)
(:generator 0
(move y x)))
(define-move-vop sap-move :move
(sap-reg) (sap-reg))
;;; Move untagged sap arguments/return-values.
(define-vop (move-sap-arg)
(:args (x :target y
:scs (sap-reg))
(fp :scs (any-reg)
:load-if (not (sc-is y sap-reg))))
(:results (y))
(:note "SAP argument move")
(:generator 0
(sc-case y
(sap-reg
(move y x))
(sap-stack
(store-stack-offset x fp y)))))
(define-move-vop move-sap-arg :move-arg
(descriptor-reg sap-reg) (sap-reg))
;;; Use standard MOVE-ARG + coercion to move an untagged sap to a
;;; descriptor passing location.
(define-move-vop move-arg :move-arg
(sap-reg) (descriptor-reg))
;;;; SAP-INT and INT-SAP
(define-vop (sap-int)
(:args (sap :scs (sap-reg) :target int))
(:arg-types system-area-pointer)
(:results (int :scs (unsigned-reg)))
(:result-types unsigned-num)
(:translate sap-int)
(:policy :fast-safe)
(:generator 1
(move int sap)))
(define-vop (int-sap)
(:args (int :scs (unsigned-reg) :target sap))
(:arg-types unsigned-num)
(:results (sap :scs (sap-reg)))
(:result-types system-area-pointer)
(:translate int-sap)
(:policy :fast-safe)
(:generator 1
(move sap int)))
;;;; POINTER+ and POINTER-
(define-vop (pointer+)
(:translate sap+)
(:args (ptr :scs (sap-reg))
(offset :scs (signed-reg)))
(:arg-types system-area-pointer signed-num)
(:results (res :scs (sap-reg)))
(:result-types system-area-pointer)
(:policy :fast-safe)
(:generator 2
(inst add res ptr offset)))
(define-vop (pointer+-unsigned-c)
(:translate sap+)
(:args (ptr :scs (sap-reg)))
(:info offset)
(:arg-types system-area-pointer (:constant (unsigned-byte 8)))
(:results (res :scs (sap-reg)))
(:result-types system-area-pointer)
(:policy :fast-safe)
(:generator 1
(inst add res ptr offset)))
(define-vop (pointer+-signed-c)
(:translate sap+)
(:args (ptr :scs (sap-reg)))
(:info offset)
(:arg-types system-area-pointer (:constant (integer -255 -1)))
(:results (res :scs (sap-reg)))
(:result-types system-area-pointer)
(:policy :fast-safe)
(:generator 1
(inst sub res ptr (- offset))))
(define-vop (pointer-)
(:translate sap-)
(:args (ptr1 :scs (sap-reg))
(ptr2 :scs (sap-reg)))
(:arg-types system-area-pointer system-area-pointer)
(:policy :fast-safe)
(:results (res :scs (signed-reg)))
(:result-types signed-num)
(:generator 1
(inst sub res ptr1 ptr2)))
;;;; mumble-SYSTEM-REF and mumble-SYSTEM-SET
(macrolet ((def-system-ref-and-set
;; NOTE: The -C VOPs have been disabled, as the allowed
;; displacements for memory references vary by
;; instruction, are confusing to figure out, and might
;; be sign-magnitude encoded. FIXME: Figure these
;; things out, and re-enable the VOPs.
(ref-name set-name sc type size &key signed use-lip)
(let ((ref-name-c (symbolicate ref-name "-C"))
(set-name-c (symbolicate set-name "-C")))
`(progn
(define-vop (,ref-name)
(:translate ,ref-name)
(:policy :fast-safe)
(:args (sap :scs (sap-reg))
(offset :scs (signed-reg)))
(:arg-types system-area-pointer signed-num)
(:results (result :scs (,sc)))
(:result-types ,type)
,@(when use-lip
'((:temporary (:sc interior-reg) lip)))
(:generator 5
,@(when use-lip
'((inst add lip sap offset)))
(inst ,(ecase size
(:byte (if signed 'ldrsb 'ldrb))
(:short (if signed 'ldrsh 'ldrh))
(:long 'ldr)
(:single 'flds)
(:double 'fldd))
result ,(if use-lip
'(@ lip)
'(@ sap offset)))))
#+(or)
(define-vop (,ref-name-c)
(:translate ,ref-name)
(:policy :fast-safe)
(:args (sap :scs (sap-reg)))
(:arg-types system-area-pointer (:constant (signed-byte 16)))
(:info offset)
(:results (result :scs (,sc)))
(:result-types ,type)
(:generator 4
(inst ,(ecase size
(:byte (if signed 'ldrsb 'ldrb))
(:short (if signed 'ldrsh 'ldrh))
(:long 'ldr)
(:single 'flds)
(:double 'fldd))
result (@ sap offset))))
(define-vop (,set-name)
(:translate ,set-name)
(:policy :fast-safe)
(:args (sap :scs (sap-reg))
(offset :scs (signed-reg))
(value :scs (,sc) :target result))
(:arg-types system-area-pointer signed-num ,type)
(:results (result :scs (,sc)))
(:result-types ,type)
,@(when use-lip
'((:temporary (:sc interior-reg) lip)))
(:generator 5
,@(when use-lip
'((inst add lip sap offset)))
(inst ,(ecase size
(:byte 'strb)
(:short 'strh)
(:long 'str)
(:single 'fsts)
(:double 'fstd))
value ,(if use-lip
'(@ lip)
'(@ sap offset)))
(unless (location= result value)
,@(case size
(:single
'((inst fcpys result value)))
(:double
'((inst fcpyd result value)))
(t
'((inst mov result value)))))))
#+(or)
(define-vop (,set-name-c)
(:translate ,set-name)
(:policy :fast-safe)
(:args (sap :scs (sap-reg))
(value :scs (,sc) :target result))
(:arg-types system-area-pointer (:constant (signed-byte 16)) ,type)
(:info offset)
(:results (result :scs (,sc)))
(:result-types ,type)
(:generator 4
(inst ,(ecase size
(:byte 'strb)
(:short 'strh)
(:long 'str)
(:single 'fsts)
(:double 'fstd))
value (@ sap offset))
(unless (location= result value)
,@(case size
(:single
'((inst fcpys result value)))
(:double
'((inst fcpyd result value)))
(t
'((inst mov result value)))))))))))
(def-system-ref-and-set sap-ref-8 %set-sap-ref-8
unsigned-reg positive-fixnum :byte :signed nil)
(def-system-ref-and-set signed-sap-ref-8 %set-signed-sap-ref-8
signed-reg tagged-num :byte :signed t)
(def-system-ref-and-set sap-ref-16 %set-sap-ref-16
unsigned-reg positive-fixnum :short :signed nil)
(def-system-ref-and-set signed-sap-ref-16 %set-signed-sap-ref-16
signed-reg tagged-num :short :signed t)
(def-system-ref-and-set sap-ref-32 %set-sap-ref-32
unsigned-reg unsigned-num :long :signed nil)
(def-system-ref-and-set signed-sap-ref-32 %set-signed-sap-ref-32
signed-reg signed-num :long :signed t)
(def-system-ref-and-set sap-ref-sap %set-sap-ref-sap
sap-reg system-area-pointer :long)
(def-system-ref-and-set sap-ref-lispobj %set-sap-ref-lispobj
descriptor-reg * :long)
(def-system-ref-and-set sap-ref-single %set-sap-ref-single
single-reg single-float :single :use-lip t)
(def-system-ref-and-set sap-ref-double %set-sap-ref-double
double-reg double-float :double :use-lip t))
;;; Noise to convert normal lisp data objects into SAPs.
(define-vop (vector-sap)
(:translate vector-sap)
(:policy :fast-safe)
(:args (vector :scs (descriptor-reg)))
(:results (sap :scs (sap-reg)))
(:result-types system-area-pointer)
(:generator 2
(inst add sap vector
(- (* vector-data-offset n-word-bytes) other-pointer-lowtag))))

View file

@ -0,0 +1,44 @@
;;;; VOPs which are useful for following the progress of the system
;;;; early in boot
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(define-vop (print)
(:args (object :scs (descriptor-reg any-reg) :target ocfp))
(:results (result :scs (descriptor-reg)))
(:save-p t)
(:temporary (:sc any-reg :offset ocfp-offset :from (:argument 0)) ocfp)
(:temporary (:sc any-reg :offset r8-offset) cfunc)
(:temporary (:scs (non-descriptor-reg)) temp)
(:temporary (:sc non-descriptor-reg :offset nargs-offset) nargs)
(:temporary (:sc control-stack :offset nfp-save-offset) nfp-save)
(:temporary (:sc interior-reg) lip)
(:vop-var vop)
(:generator 100
(let ((call-into-c-fixup (gen-label))
(debug-print-fixup (gen-label))
(cur-nfp (current-nfp-tn vop)))
(assemble (*elsewhere*)
(emit-label call-into-c-fixup)
(inst word (make-fixup "call_into_c" :foreign))
(emit-label debug-print-fixup)
(inst word (make-fixup "debug_print" :foreign)))
(when cur-nfp
(store-stack-tn nfp-save cur-nfp))
(move ocfp object)
(inst load-from-label temp lip call-into-c-fixup)
(inst load-from-label cfunc lip debug-print-fixup)
(inst blx temp)
(when cur-nfp
(load-stack-tn cur-nfp nfp-save))
(move result nargs))))

View file

@ -0,0 +1,144 @@
;;;; VOPs and macro magic for calling static functions
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(define-vop (static-fun-template)
(:save-p t)
(:policy :safe)
(:variant-vars symbol)
(:vop-var vop)
(:temporary (:scs (non-descriptor-reg)) temp)
(:temporary (:scs (descriptor-reg)) move-temp)
(:temporary (:scs (descriptor-reg)) function)
(:temporary (:sc any-reg :offset nargs-offset) nargs)
(:temporary (:sc interior-reg) lip)
(:temporary (:sc control-stack :offset nfp-save-offset) nfp-save))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun static-fun-template-name (num-args num-results)
(intern (format nil "~:@(~R-arg-~R-result-static-fun~)"
num-args num-results)))
(defun moves (dst src)
(collect ((moves))
(do ((dst dst (cdr dst))
(src src (cdr src)))
((or (null dst) (null src)))
(moves `(move ,(car dst) ,(car src))))
(moves)))
(defun static-fun-template-vop (num-args num-results)
(unless (and (<= num-args register-arg-count)
(<= num-results register-arg-count))
(error "either too many args (~W) or too many results (~W); max = ~W"
num-args num-results register-arg-count))
(let ((num-temps (max num-args num-results)))
(collect ((temp-names) (temps) (arg-names) (args) (result-names) (results))
(dotimes (i num-results)
(let ((result-name (intern (format nil "RESULT-~D" i))))
(result-names result-name)
(results `(,result-name :scs (any-reg descriptor-reg)))))
(dotimes (i num-temps)
(let ((temp-name (intern (format nil "TEMP-~D" i))))
(temp-names temp-name)
(temps `(:temporary (:sc descriptor-reg
:offset ,(nth i *register-arg-offsets*)
,@(when (< i num-args)
`(:from (:argument ,i)))
,@(when (< i num-results)
`(:to (:result ,i)
:target ,(nth i (result-names)))))
,temp-name))))
(dotimes (i num-args)
(let ((arg-name (intern (format nil "ARG-~D" i))))
(arg-names arg-name)
(args `(,arg-name
:scs (any-reg descriptor-reg)
:target ,(nth i (temp-names))))))
`(define-vop (,(static-fun-template-name num-args num-results)
static-fun-template)
(:args ,@(args))
,@(temps)
(:temporary (:sc any-reg) csp-temp)
(:results ,@(results))
(:generator ,(+ 50 num-args num-results)
(let ((lra-label (gen-label))
(cur-nfp (current-nfp-tn vop)))
,@(moves (temp-names) (arg-names))
(inst ldr function (@ null-tn (static-fun-offset symbol)))
(inst mov nargs (fixnumize ,num-args))
(when cur-nfp
(store-stack-tn nfp-save cur-nfp))
;; This is a somewhat ideosyncratic way to build a new
;; stack frame, pushing a value and updating CSP, finding
;; the new CFP, then pushing another value on CSP, but it
;; works for this situation.
(inst compute-lra lip lip lra-label)
(load-csp csp-temp)
(inst add csp-temp csp-temp 8)
(store-csp csp-temp)
(inst str cfp-tn (@ csp-temp -8))
(inst str lip (@ csp-temp -4))
(inst sub cfp-tn csp-temp 8)
(note-this-location vop :call-site)
(lisp-jump function)
(emit-return-pc lra-label)
,(collect ((bindings) (links))
(do ((temp (temp-names) (cdr temp))
(name 'values (gensym))
(prev nil name)
(i 0 (1+ i)))
((= i num-results))
(bindings `(,name
(make-tn-ref ,(car temp) nil)))
(when prev
(links `(setf (tn-ref-across ,prev) ,name))))
`(let ,(bindings)
,@(links)
(default-unknown-values vop
,(if (zerop num-results) nil 'values)
,num-results move-temp temp lip lra-label)))
(when cur-nfp
(load-stack-tn cur-nfp nfp-save))
,@(moves (result-names) (temp-names))))))))
) ; EVAL-WHEN
(macrolet ((frob (num-args num-res)
(static-fun-template-vop (eval num-args) (eval num-res))))
;; Other backends cover options from zero through
;; register-arg-count. It turns out, however, that only the 1 and 2
;; arg cases are actually used.
(frob 1 1)
(frob 2 1))
(defmacro define-static-fun (name args &key (results '(x)) translate
policy cost arg-types result-types)
`(define-vop (,name
,(static-fun-template-name (length args)
(length results)))
(:variant ',name)
(:note ,(format nil "static-fun ~@(~S~)" name))
,@(when translate
`((:translate ,translate)))
,@(when policy
`((:policy ,policy)))
,@(when cost
`((:generator-cost ,cost)))
,@(when arg-types
`((:arg-types ,@arg-types)))
,@(when result-types
`((:result-types ,@result-types)))))

View file

@ -0,0 +1,50 @@
;;;; linkage information for standard static functions, and
;;;; miscellaneous VOPs
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(define-vop (length/list)
(:translate length)
(:args (object :scs (descriptor-reg) :target ptr))
(:arg-types list)
(:temporary (:scs (descriptor-reg) :from (:argument 0)) ptr)
(:temporary (:scs (non-descriptor-reg)) temp)
(:temporary (:scs (any-reg) :type fixnum :to (:result 0) :target result)
count)
(:results (result :scs (any-reg descriptor-reg)))
(:policy :fast-safe)
(:vop-var vop)
(:save-p :compute-only)
(:generator 50
(let ((done (gen-label))
(loop (gen-label))
(not-list (gen-label)))
(move ptr object)
(inst eor count count count)
(emit-label loop)
(inst cmp ptr null-tn)
(inst b :eq done)
(test-type ptr not-list t (list-pointer-lowtag) :temp temp)
(loadw ptr ptr cons-cdr-slot list-pointer-lowtag)
(inst add count count (fixnumize 1))
(test-type ptr loop nil (list-pointer-lowtag) :temp temp)
(emit-label not-list)
(error-call vop 'object-not-list-error ptr)
(emit-label done)
(move result count))))

View file

@ -0,0 +1,259 @@
;;;; ARM VM definitions of various system hacking operations
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; Type frobbing VOPs
(define-vop (lowtag-of)
(:translate lowtag-of)
(:policy :fast-safe)
(:args (object :scs (any-reg descriptor-reg)))
(:results (result :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:generator 1
(inst and result object lowtag-mask)))
(define-vop (widetag-of)
(:translate widetag-of)
(:policy :fast-safe)
(:args (object :scs (descriptor-reg) :to (:eval 1)))
(:results (result :scs (unsigned-reg) :from (:eval 0)))
(:result-types positive-fixnum)
(:generator 6
;; First, pick off the immediate types, starting with FIXNUM.
(inst ands result object fixnum-tag-mask)
;; If it wasn't a fixnum, start with the full widetag.
(inst and :ne result object widetag-mask)
;; Now, we have our result for an immediate type, but we might
;; have a pointer object instead, in which case we need to do more
;; work. Check for a pointer type.
;; KLUDGE: We're a 32-bit port, so all pointer lowtags have the
;; low bit set, but there's no obvious named constant for this.
;; On 64-bit ports, all pointer lowtags have the low two bits set,
;; so this wouldn't work as easily.
(inst tst object 1)
;; If we have a pointer type, we need to compute a different
;; answer. For lists and instances, we just need the lowtag. For
;; functions and "other", we need to load the widetag from the
;; object header. In both cases, having just the widetag
;; available is handy.
(inst and :ne result object lowtag-mask)
;; We now have the correct answer for list-pointer-lowtag and
;; instance-pointer-lowtag, but need to pick off the case for the
;; other two pointer types. KLUDGE: FUN-POINTER-LOWTAG and
;; OTHER-POINTER-LOWTAG are both in the upper half of the lowtag
;; space, while LIST-POINTER-LOWTAG and INSTANCE-POINTER-LOWTAG
;; are in the lower half, so we distinguish with a bit test.
(inst tst :ne object 4)
;; We can't use both register and immediate offsets in the same
;; load/store instruction, so we need to bias our register offset
;; on big-endian systems.
(when (eq *backend-byte-order* :big-endian)
(inst sub :ne result (1- n-word-bytes)))
;; And, finally, pick out the widetag from the header.
(inst ldrb :ne result (@ object (- result)))))
(define-vop (fun-subtype)
(:translate fun-subtype)
(:policy :fast-safe)
(:args (function :scs (descriptor-reg)))
(:results (result :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:generator 6
(load-type result function (- fun-pointer-lowtag))))
(define-vop (set-fun-subtype)
(:translate (setf fun-subtype))
(:policy :fast-safe)
(:args (type :scs (unsigned-reg) :target result)
(function :scs (descriptor-reg)))
(:arg-types positive-fixnum *)
(:results (result :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:generator 6
(inst strb type (@ function (- (ecase *backend-byte-order*
(:little-endian 0)
(:big-endian (1- n-word-bytes)))
fun-pointer-lowtag)))
(move result type)))
(define-vop (get-header-data)
(:translate get-header-data)
(:policy :fast-safe)
(:args (x :scs (descriptor-reg)))
(:results (res :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:generator 6
(loadw res x 0 other-pointer-lowtag)
(inst mov res (lsr res n-widetag-bits))))
(define-vop (get-closure-length)
(:translate get-closure-length)
(:policy :fast-safe)
(:args (x :scs (descriptor-reg)))
(:results (res :scs (unsigned-reg)))
(:result-types positive-fixnum)
(:generator 6
(loadw res x 0 fun-pointer-lowtag)
(inst mov res (lsr res n-widetag-bits))))
(define-vop (set-header-data)
(:translate set-header-data)
(:policy :fast-safe)
(:args (x :scs (descriptor-reg) :target res)
(data :scs (any-reg immediate)))
(:arg-types * positive-fixnum)
(:results (res :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) t1)
(:generator 6
(load-type t1 x (- other-pointer-lowtag))
(sc-case data
(any-reg
(inst orr t1 t1 (lsl data (- n-widetag-bits n-fixnum-tag-bits))))
(immediate
;; FIXME: This will break if DATA has bits spread over more
;; than an eight bit range aligned on an even bit position.
;; See SYS:SRC;COMPILER;ARM;MOVE.LISP for a partial fix... And
;; maybe it should be promoted to an instruction-macro?
(inst orr t1 t1 (ash (tn-value data) n-widetag-bits))))
(storew t1 x 0 other-pointer-lowtag)
(move res x)))
(define-vop (pointer-hash)
(:translate pointer-hash)
(:args (ptr :scs (any-reg descriptor-reg)))
(:results (res :scs (any-reg descriptor-reg)))
(:policy :fast-safe)
(:generator 1
(inst bic res ptr lowtag-mask)
(inst mov res (lsr res 1))))
;;;; Allocation
(define-vop (dynamic-space-free-pointer)
(:results (int :scs (sap-reg)))
(:result-types system-area-pointer)
(:translate dynamic-space-free-pointer)
(:policy :fast-safe)
(:generator 1
(load-symbol-value int *allocation-pointer*)))
(define-vop (binding-stack-pointer-sap)
(:results (int :scs (sap-reg)))
(:result-types system-area-pointer)
(:translate binding-stack-pointer-sap)
(:policy :fast-safe)
(:generator 1
(load-symbol-value int *binding-stack-pointer*)))
(define-vop (control-stack-pointer-sap)
(:results (int :scs (sap-reg)))
(:result-types system-area-pointer)
(:translate control-stack-pointer-sap)
(:policy :fast-safe)
(:generator 1
(load-csp int)))
;;;; Code object frobbing.
(define-vop (code-instructions)
(:translate code-instructions)
(:policy :fast-safe)
(:args (code :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) ndescr)
(:results (sap :scs (sap-reg)))
(:result-types system-area-pointer)
(:generator 10
(loadw ndescr code 0 other-pointer-lowtag)
;; CODE-HEADER-WIDETAG is #x38, which has the top two bits clear,
;; so we don't to clear the low bits here. If we do, use BIC.
(inst mov ndescr (lsr ndescr (- n-widetag-bits word-shift)))
(inst sub ndescr ndescr other-pointer-lowtag)
(inst add sap code ndescr)))
(define-vop (compute-fun)
(:args (code :scs (descriptor-reg))
(offset :scs (signed-reg unsigned-reg)))
(:arg-types * positive-fixnum)
(:results (func :scs (descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) ndescr)
(:generator 10
(loadw ndescr code 0 other-pointer-lowtag)
;; CODE-HEADER-WIDETAG is #x38, which has the top two bits clear,
;; so we don't to clear the low bits here. If we do, use BIC.
(inst add ndescr offset (lsr ndescr (- n-widetag-bits word-shift)))
(inst sub ndescr ndescr (- other-pointer-lowtag fun-pointer-lowtag))
(inst add func code ndescr)))
;;;
#!+symbol-info-vops
(define-vop (symbol-info-vector)
(:policy :fast-safe)
(:translate symbol-info-vector)
(:args (x :scs (descriptor-reg)))
(:results (res :scs (descriptor-reg)))
(:temporary (:sc unsigned-reg) temp)
(:generator 1
(loadw res x symbol-info-slot other-pointer-lowtag)
;; If RES has list-pointer-lowtag, take its CDR. If not, use it as-is.
(inst and temp res lowtag-mask)
(inst cmp temp list-pointer-lowtag)
(loadw res res cons-cdr-slot list-pointer-lowtag :eq)))
#!+symbol-info-vops
(define-vop (symbol-plist)
(:policy :fast-safe)
(:translate symbol-plist)
(:args (x :scs (descriptor-reg)))
(:results (res :scs (descriptor-reg)))
(:generator 1
(loadw res x symbol-info-slot other-pointer-lowtag)
;; Instruction pun: (CAR x) is the same as (VECTOR-LENGTH x)
;; so if the info slot holds a vector, this gets a fixnum- it's not a plist.
(loadw res res cons-car-slot list-pointer-lowtag)
(inst tst res fixnum-tag-mask)
(inst mov :eq res null-tn)))
;;;; other miscellaneous VOPs
(defknown sb!unix::receive-pending-interrupt () (values))
(define-vop (sb!unix::receive-pending-interrupt)
(:policy :fast-safe)
(:translate sb!unix::receive-pending-interrupt)
(:generator 1
(inst debug-trap)
(inst byte pending-interrupt-trap)
(emit-alignment word-shift)))
(define-vop (halt)
(:temporary (:sc non-descriptor-reg :offset ocfp-offset) error-temp)
(:generator 1
;; See macros.lisp, EMIT-ERROR-BREAK, for an explanation.
(inst mov error-temp #x000f0000)
(inst add error-temp error-temp 1)
(inst swi 0)
(inst byte halt-trap)
;; Re-align to the next instruction boundary.
(emit-alignment word-shift)))
;;;; Dummy definition for a spin-loop hint VOP
(define-vop (spin-loop-hint)
(:translate spin-loop-hint)
(:policy :fast-safe)
(:generator 0))

View file

@ -0,0 +1,3 @@
(in-package "SB!VM")
;;; Let's see if an empty file works here. It does on the Alpha.

View file

@ -0,0 +1,422 @@
;;;; type testing and checking VOPs for the ARM VM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(defun %test-fixnum (value target not-p &key temp)
(declare (ignore temp))
(assemble ()
(inst tst value fixnum-tag-mask)
(inst b (if not-p :ne :eq) target)))
(defun %test-fixnum-and-headers (value target not-p headers &key temp)
(let ((drop-through (gen-label)))
(assemble ()
(inst ands temp value fixnum-tag-mask)
(inst b :eq (if not-p drop-through target)))
(%test-headers value target not-p nil headers
:drop-through drop-through :temp temp)))
(defun %test-immediate (value target not-p immediate &key temp)
(assemble ()
(inst and temp value widetag-mask)
(inst cmp temp immediate)
(inst b (if not-p :ne :eq) target)))
(defun %test-lowtag (value target not-p lowtag &key temp)
(assemble ()
(inst and temp value lowtag-mask)
(inst cmp temp lowtag)
(inst b (if not-p :ne :eq) target)))
(defun %test-headers (value target not-p function-p headers
&key temp (drop-through (gen-label)))
(let ((lowtag (if function-p fun-pointer-lowtag other-pointer-lowtag)))
(multiple-value-bind (when-true when-false)
(if not-p
(values drop-through target)
(values target drop-through))
(assemble ()
(%test-lowtag value when-false t lowtag :temp temp)
(load-type temp value (- lowtag))
(do ((remaining headers (cdr remaining)))
((null remaining))
(let ((header (car remaining))
(last (null (cdr remaining))))
(cond
((atom header)
(cond
((and (not last) (null (cddr remaining))
(atom (cadr remaining))
(= (logcount (logxor header (cadr remaining))) 1))
(inst and temp temp (ldb (byte 8 0) (logeqv header (cadr remaining))))
(inst cmp temp (ldb (byte 8 0) (logand header (cadr remaining))))
(inst b (if not-p :ne :eq) target)
(return))
(t
(inst cmp temp header)
(if last
(inst b (if not-p :ne :eq) target)
(inst b :eq when-true)))))
(t
(let ((start (car header))
(end (cdr header)))
(cond
((and last (not (= start bignum-widetag))
(= (+ start 4) end)
(= (logcount (logxor start end)) 1))
(inst and temp temp (ldb (byte 8 0) (logeqv start end)))
(inst cmp temp (ldb (byte 8 0) (logand start end)))
(inst b (if not-p :ne :eq) target))
((and (not last) (null (cddr remaining))
(= (+ start 4) end) (= (logcount (logxor start end)) 1)
(listp (cadr remaining))
(= (+ (caadr remaining) 4) (cdadr remaining))
(= (logcount (logxor (caadr remaining) (cdadr remaining))) 1)
(= (logcount (logxor (caadr remaining) start)) 1))
(inst and temp temp (ldb (byte 8 0) (logeqv start (cdadr remaining))))
(inst cmp temp (ldb (byte 8 0) (logand start (cdadr remaining))))
(inst b (if not-p :ne :eq) target)
(return))
(t
(unless (= start bignum-widetag)
(inst cmp temp start)
(if (= end complex-array-widetag)
(progn
(aver last)
(inst b (if not-p :lt :ge) target))
(inst b :lt when-false)))
(unless (= end complex-array-widetag)
(inst cmp temp end)
(if last
(inst b (if not-p :gt :le) target)
(inst b :le when-true))))))))))
(emit-label drop-through)))))
;;; Type checking and testing (see also the use of !DEFINE-TYPE-VOPS
;;; in src/compiler/generic/late-type-vops.lisp):
;;;
;;; [FIXME: Like some of the other comments in this file, this one
;;; really belongs somewhere else]
(define-vop (check-type)
(:args (value :target result :scs (any-reg descriptor-reg)))
(:results (result :scs (any-reg descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)
:to (:result 0)
:offset ocfp-offset)
temp)
(:vop-var vop)
(:save-p :compute-only))
(define-vop (type-predicate)
(:args (value :scs (any-reg descriptor-reg)))
(:temporary (:scs (non-descriptor-reg)) temp)
(:conditional)
(:info target not-p)
(:policy :fast-safe))
(defun cost-to-test-types (type-codes)
(+ (* 2 (length type-codes))
(if (> (apply #'max type-codes) lowtag-limit) 7 2)))
(defmacro !define-type-vops (pred-name check-name ptype error-code
(&rest type-codes)
&key &allow-other-keys)
(let ((cost (cost-to-test-types (mapcar #'eval type-codes))))
`(progn
,@(when pred-name
`((define-vop (,pred-name type-predicate)
(:translate ,pred-name)
(:generator ,cost
(test-type value target not-p (,@type-codes)
:temp temp)))))
,@(when check-name
`((define-vop (,check-name check-type)
(:generator ,cost
(let ((err-lab
(generate-error-code vop ',error-code value)))
(test-type value err-lab t (,@type-codes)
:temp temp)
(move result value))))))
,@(when ptype
`((primitive-type-vop ,check-name (:check) ,ptype))))))
;;;; Other integer ranges.
;;; A (signed-byte 32) can be represented with either fixnum or a bignum with
;;; exactly one digit.
(defun signed-byte-32-test (value temp not-p target not-target)
(multiple-value-bind
(yep nope)
(if not-p
(values not-target target)
(values target not-target))
(assemble ()
(inst ands temp value fixnum-tag-mask)
(inst b :eq yep)
(test-type value nope t (other-pointer-lowtag) :temp temp)
(loadw temp value 0 other-pointer-lowtag)
;; (+ (ash 1 n-widetag-bits) bignum-widetag) does not fit into a single immediate
(inst eor temp temp (ash 1 n-widetag-bits))
(inst eors temp temp bignum-widetag)
(inst b (if not-p :ne :eq) target)))
(values))
(define-vop (signed-byte-32-p type-predicate)
(:translate signed-byte-32-p)
(:generator 45
(let ((not-target (gen-label)))
(signed-byte-32-test value temp not-p target not-target)
(emit-label not-target))))
(define-vop (check-signed-byte-32 check-type)
(:generator 45
(let ((nope (generate-error-code vop 'object-not-signed-byte-32-error value))
(yep (gen-label)))
(signed-byte-32-test value temp t nope yep)
(emit-label yep)
(move result value))))
;;; An (UNSIGNED-BYTE 32) can be represented with either a positive
;;; fixnum, a bignum with exactly one positive digit, or a bignum with
;;; exactly two digits and the second digit all zeros.
(defun unsigned-byte-32-test (value temp not-p target not-target)
(let ((single-word (gen-label))
(fixnum (gen-label)))
(multiple-value-bind (yep nope)
(if not-p
(values not-target target)
(values target not-target))
(assemble ()
;; Is it a fixnum?
(move temp value)
(%test-fixnum temp fixnum nil)
;; If not, is it an other pointer?
(test-type value nope t (other-pointer-lowtag) :temp temp)
;; Get the header.
(loadw temp value 0 other-pointer-lowtag)
;; Is it one?
;; (+ (ash 1 n-widetag-bits) bignum-widetag) does not fit into a single immediate
(inst eor temp temp (ash 1 n-widetag-bits))
(inst eors temp temp bignum-widetag)
(inst b :eq single-word)
;; If it's other than two, we can't be an (unsigned-byte 32)
(inst eors temp temp (logxor (+ (ash 1 n-widetag-bits) bignum-widetag)
(+ (ash 2 n-widetag-bits) bignum-widetag)))
(inst b :ne nope)
;; Get the second digit.
(loadw temp value (1+ bignum-digits-offset) other-pointer-lowtag)
;; All zeros, its an (unsigned-byte 32).
(inst cmp temp 0)
(inst b :eq yep)
(inst b nope)
(emit-label single-word)
;; Get the single digit.
(loadw temp value bignum-digits-offset other-pointer-lowtag)
;; positive implies (unsigned-byte 32).
(emit-label fixnum)
(inst cmp temp 0)
(if not-p
(inst b :lt target)
(inst b :ge target))))
(values)))
(define-vop (unsigned-byte-32-p type-predicate)
(:translate unsigned-byte-32-p)
(:generator 45
(let ((not-target (gen-label)))
(unsigned-byte-32-test value temp not-p target not-target)
(emit-label not-target))))
(define-vop (check-unsigned-byte-32 check-type)
(:generator 45
(let ((lose (generate-error-code vop 'object-not-unsigned-byte-32-error value))
(okay (gen-label)))
(unsigned-byte-32-test value temp t lose okay)
(emit-label okay)
(move result value))))
;;; MOD type checks
(defun power-of-two-limit-p (x)
(and (fixnump x)
(= (logcount (1+ x)) 1)
;; Immediate encodable
(> x (expt 2 23))))
(define-vop (test-fixnum-mod-power-of-two)
(:args (value :scs (any-reg descriptor-reg
unsigned-reg signed-reg
immediate)))
(:arg-types *
(:constant (satisfies power-of-two-limit-p)))
(:translate fixnum-mod-p)
(:conditional :eq)
(:info hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 2
(aver (not (sc-is value immediate)))
(let* ((fixnum-hi (if (sc-is value unsigned-reg signed-reg)
hi
(fixnumize hi))))
(inst tst value (lognot fixnum-hi)))))
(define-vop (test-fixnum-mod-tagged-unsigned-imm)
(:args (value :scs (any-reg descriptor-reg
unsigned-reg signed-reg
immediate)))
(:arg-types (:or tagged-num unsigned-num signed-num)
(:constant (satisfies encodable-immediate)))
(:translate fixnum-mod-p)
(:conditional :ls)
(:info hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 3
(aver (not (sc-is value immediate)))
(let ((fixnum-hi (if (sc-is value unsigned-reg signed-reg)
hi
(fixnumize hi))))
(inst cmp value fixnum-hi))))
(defun encodable-immediate+1 (x)
(encodable-immediate (1+ x)))
;;; Adding 1 and changing the codntions from <= to < allows to encode
;;; more immediates.
(define-vop (test-fixnum-mod-tagged-unsigned-imm+1)
(:args (value :scs (any-reg descriptor-reg
unsigned-reg signed-reg
immediate)))
(:arg-types (:or tagged-num unsigned-num signed-num)
(:constant (satisfies encodable-immediate+1)))
(:translate fixnum-mod-p)
(:conditional :cc)
(:info hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 3
(aver (not (sc-is value immediate)))
(let ((fixnum-hi (if (sc-is value unsigned-reg signed-reg)
(1+ hi)
(fixnumize (1+ hi)))))
(inst cmp value fixnum-hi))))
(define-vop (test-fixnum-mod-tagged-unsigned)
(:args (value :scs (any-reg descriptor-reg
unsigned-reg signed-reg
immediate)))
(:arg-types (:or tagged-num unsigned-num signed-num)
(:constant fixnum))
(:temporary (:scs (non-descriptor-reg)) temp)
(:translate fixnum-mod-p)
(:conditional :ls)
(:info hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 4
(aver (not (sc-is value immediate)))
(let ((fixnum-hi (if (sc-is value unsigned-reg signed-reg)
hi
(fixnumize hi))))
(load-immediate-word temp fixnum-hi)
(inst cmp value temp))))
(defun encodable-immediate/+1 (x)
(or (encodable-immediate x)
(encodable-immediate (1+ x))))
(define-vop (test-fixnum-mod-*-imm)
(:args (value :scs (any-reg descriptor-reg)))
(:arg-types * (:constant (satisfies encodable-immediate/+1)))
(:translate fixnum-mod-p)
(:conditional)
(:info target not-p hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 5
(let* ((1+ (not (encodable-immediate hi)))
(fixnum-hi (fixnumize (if 1+
(1+ hi)
hi)))
(skip (gen-label)))
(inst tst value fixnum-tag-mask)
(inst b :ne (if not-p target skip))
(inst cmp value fixnum-hi)
(inst b (if not-p
(if 1+ :cs :hi)
(if 1+ :cc :ls))
target)
(emit-label SKIP))))
(define-vop (test-fixnum-mod-*)
(:args (value :scs (any-reg descriptor-reg)))
(:arg-types * (:constant fixnum))
(:translate fixnum-mod-p)
(:temporary (:scs (any-reg)) temp)
(:conditional)
(:info target not-p hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 6
(inst tst value fixnum-tag-mask)
(inst b :ne (if not-p target skip))
(let ((condition (if not-p :hi :ls)))
(load-immediate-word temp (fixnumize hi))
(inst cmp value temp)
(inst b condition target))
SKIP))
;;;; List/symbol types:
;;;
;;; symbolp (or symbol (eq nil))
;;; consp (and list (not (eq nil)))
(define-vop (symbolp type-predicate)
(:translate symbolp)
(:generator 12
(let* ((drop-thru (gen-label))
(is-symbol-label (if not-p drop-thru target)))
(inst cmp value null-tn)
(inst b :eq is-symbol-label)
(test-type value target not-p (symbol-header-widetag) :temp temp)
(emit-label drop-thru))))
(define-vop (check-symbol check-type)
(:generator 12
(let ((drop-thru (gen-label))
(error (generate-error-code vop 'object-not-symbol-error value)))
(inst cmp value null-tn)
(inst b :eq drop-thru)
(test-type value error t (symbol-header-widetag) :temp temp)
(emit-label drop-thru)
(move result value))))
(define-vop (consp type-predicate)
(:translate consp)
(:generator 8
(let* ((drop-thru (gen-label))
(is-not-cons-label (if not-p target drop-thru)))
(inst cmp value null-tn)
(inst b :eq is-not-cons-label)
(test-type value target not-p (list-pointer-lowtag) :temp temp)
(emit-label drop-thru))))
(define-vop (check-cons check-type)
(:generator 8
(let ((error (generate-error-code vop 'object-not-cons-error value)))
(inst cmp value null-tn)
(inst b :eq error)
(test-type value error t (list-pointer-lowtag) :temp temp)
(move result value))))

View file

@ -0,0 +1,153 @@
;;;; unknown-values VOPs for the ARM VM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
(define-vop (reset-stack-pointer)
(:args (ptr :scs (any-reg)))
(:generator 1
(store-csp ptr)))
(define-vop (%%nip-values)
(:args (last-nipped-ptr :scs (any-reg) :target dest)
(last-preserved-ptr :scs (any-reg) :target src)
(moved-ptrs :scs (any-reg) :more t))
(:results (r-moved-ptrs :scs (any-reg) :more t))
(:temporary (:sc any-reg) src)
(:temporary (:sc any-reg) dest)
(:temporary (:sc non-descriptor-reg) temp)
(:temporary (:sc any-reg) stack-pointer)
(:ignore r-moved-ptrs)
(:generator 1
(move src last-preserved-ptr)
(move dest last-nipped-ptr)
(load-csp stack-pointer)
(inst cmp stack-pointer src)
(inst b :le DONE)
LOOP
(loadw temp src)
(inst add dest dest n-word-bytes)
(inst add src src n-word-bytes)
(storew temp dest -1)
(inst cmp stack-pointer src)
(inst b :gt LOOP)
DONE
(store-csp dest)
(inst sub src src dest)
(loop for moved = moved-ptrs then (tn-ref-across moved)
while moved
do (sc-case (tn-ref-tn moved)
((descriptor-reg any-reg)
(inst sub (tn-ref-tn moved) (tn-ref-tn moved) src))
((control-stack)
(load-stack-tn temp (tn-ref-tn moved))
(inst sub temp temp src)
(store-stack-tn (tn-ref-tn moved) temp))))))
;;; Push some values onto the stack, returning the start and number of values
;;; pushed as results. It is assumed that the Vals are wired to the standard
;;; argument locations. Nvals is the number of values to push.
;;;
;;; The generator cost is pseudo-random. We could get it right by defining a
;;; bogus SC that reflects the costs of the memory-to-memory moves for each
;;; operand, but this seems unworthwhile.
;;;
(define-vop (push-values)
(:args (vals :more t))
(:results (start :scs (any-reg) :from :load)
(count :scs (any-reg)))
(:info nvals)
(:temporary (:scs (descriptor-reg)) temp)
(:generator 20
(load-csp start)
(inst add temp start (* nvals n-word-bytes))
(store-csp temp)
(do ((val vals (tn-ref-across val))
(i 0 (1+ i)))
((null val))
(let ((tn (tn-ref-tn val)))
(sc-case tn
(descriptor-reg
(storew tn start i))
(control-stack
(load-stack-tn temp tn)
(storew temp start i)))))
(inst mov count (fixnumize nvals))))
;;; Push a list of values on the stack, returning Start and Count as used in
;;; unknown values continuations.
;;;
(define-vop (values-list)
(:args (arg :scs (descriptor-reg) :target list))
(:arg-types list)
(:policy :fast-safe)
(:results (start :scs (any-reg))
(count :scs (any-reg)))
(:temporary (:scs (descriptor-reg) :type list :from (:argument 0)) list)
(:temporary (:scs (descriptor-reg)) temp)
(:temporary (:scs (non-descriptor-reg)) ndescr)
(:temporary (:sc any-reg) csp-temp)
(:vop-var vop)
(:save-p :compute-only)
(:generator 0
(move list arg)
(load-csp start)
(move csp-temp start)
LOOP
(inst cmp list null-tn)
(loadw temp list cons-car-slot list-pointer-lowtag)
(inst b :eq DONE)
(loadw list list cons-cdr-slot list-pointer-lowtag)
(inst add csp-temp csp-temp n-word-bytes)
(store-csp csp-temp)
(storew temp csp-temp -1)
(test-type list LOOP nil (list-pointer-lowtag) :temp ndescr)
(error-call vop 'bogus-arg-to-values-list-error list)
DONE
(inst sub count csp-temp start)))
;;; Copy the more arg block to the top of the stack so we can use them
;;; as function arguments.
;;;
(define-vop (%more-arg-values)
(:args (context :scs (descriptor-reg any-reg) :target src)
(skip :scs (any-reg immediate))
(num :scs (any-reg) :target count))
(:arg-types * positive-fixnum positive-fixnum)
(:temporary (:sc any-reg :from (:argument 0)) src)
(:temporary (:sc any-reg :from (:argument 2)) dst)
(:temporary (:sc descriptor-reg :from (:argument 1)) temp)
(:temporary (:sc any-reg) i)
(:results (start :scs (any-reg))
(count :scs (any-reg)))
(:generator 20
(sc-case skip
(immediate
(inst add src context (* (tn-value skip) n-word-bytes)))
(any-reg
(inst add src context skip)))
(inst adds count num 0)
(load-csp start)
(inst b :eq DONE)
(inst mov dst start)
(inst add i start count)
(store-csp i)
(inst mov i count)
LOOP
(inst cmp i 4)
(inst sub i i 4)
(inst ldr temp (@ src i))
(inst str temp (@ dst i))
(inst b :ne LOOP)
DONE))

327
src/compiler/arm64/vm.lisp Normal file
View file

@ -0,0 +1,327 @@
;;;; miscellaneous VM definition noise for the ARM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; register specs
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *register-names* (make-array 16 :initial-element nil)))
(macrolet ((defreg (name offset)
(let ((offset-sym (symbolicate name "-OFFSET")))
`(eval-when (:compile-toplevel :load-toplevel :execute)
(def!constant ,offset-sym ,offset)
(setf (svref *register-names* ,offset-sym) ,(symbol-name name)))))
(defregset (name &rest regs)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(defparameter ,name
(list ,@(mapcar #'(lambda (name)
(symbolicate name "-OFFSET")) regs))))))
(defreg r0 0)
(defreg r1 1)
(defreg r2 2)
(defreg lexenv 3)
(defreg nl2 4)
(defreg code 5)
(defreg nl3 6)
(defreg ocfp 7)
(defreg r8 8)
(defreg nfp 9)
(defreg null 10)
(defreg cfp 11)
(defreg nargs 12)
(defreg nsp 13)
(defreg lr 14)
(defreg pc 15) ;; Yes, the program counter.
(defregset system-regs
null cfp nsp lr pc code)
(defregset descriptor-regs
r0 r1 r2 lexenv r8)
(defregset non-descriptor-regs
ocfp nfp nargs nl2 nl3)
;; registers used to pass arguments
;;
;; the number of arguments/return values passed in registers
(def!constant register-arg-count 3)
;; names and offsets for registers used to pass arguments
(defregset *register-arg-offsets* r0 r1 r2)
(defparameter *register-arg-names* '(r0 r1 r2)))
;;;; SB and SC definition:
(define-storage-base registers :finite :size 16)
(define-storage-base control-stack :unbounded :size 2 :size-increment 1)
(define-storage-base non-descriptor-stack :unbounded :size 0)
(define-storage-base constant :non-packed)
(define-storage-base immediate-constant :non-packed)
#!+arm-vfp
(define-storage-base float-registers :finite :size 32)
;; NOTE: If you fix the following, please to so with its own feature
;; conditional, and also adjust the definitions of the
;; {,COMPLEX-}{SINGLE,DOUBLE}-REG SCs below.
#!-arm-vfp
(error "Don't know how many float registers for non-VFP systems")
;;;
;;; Handy macro so we don't have to keep changing all the numbers whenever
;;; we insert a new storage class.
;;;
(defmacro define-storage-classes (&rest classes)
(do ((forms (list 'progn)
(let* ((class (car classes))
(sc-name (car class))
(constant-name (intern (concatenate 'simple-string
(string sc-name)
"-SC-NUMBER"))))
(list* `(define-storage-class ,sc-name ,index
,@(cdr class))
`(def!constant ,constant-name ,index)
forms)))
(index 0 (1+ index))
(classes classes (cdr classes)))
((null classes)
(nreverse forms))))
(define-storage-classes
;; Non-immediate contstants in the constant pool
(constant constant)
;; NULL is in a register.
(null immediate-constant)
;; Anything else that can be an immediate.
(immediate immediate-constant)
;; **** The stacks.
;; The control stack. (Scanned by GC)
(control-stack control-stack)
;; We put ANY-REG and DESCRIPTOR-REG early so that their SC-NUMBER
;; is small and therefore the error trap information is smaller.
;; Moving them up here from their previous place down below saves
;; ~250K in core file size. --njf, 2006-01-27
;; Immediate descriptor objects. Don't have to be seen by GC, but nothing
;; bad will happen if they are. (fixnums, characters, header values, etc).
(any-reg
registers
:locations #.(append non-descriptor-regs descriptor-regs)
:constant-scs (immediate)
:save-p t
:alternate-scs (control-stack))
;; Pointer descriptor objects. Must be seen by GC.
(descriptor-reg registers
:locations #.descriptor-regs
:constant-scs (constant null immediate)
:save-p t
:alternate-scs (control-stack))
;; The non-descriptor stacks.
(signed-stack non-descriptor-stack) ; (signed-byte 32)
(unsigned-stack non-descriptor-stack) ; (unsigned-byte 32)
(character-stack non-descriptor-stack) ; non-descriptor characters.
(sap-stack non-descriptor-stack) ; System area pointers.
(single-stack non-descriptor-stack) ; single-floats
(double-stack non-descriptor-stack
:element-size 2 :alignment 2) ; double floats.
(complex-single-stack non-descriptor-stack :element-size 2)
(complex-double-stack non-descriptor-stack :element-size 4 :alignment 2)
;; **** Things that can go in the integer registers.
;; Non-Descriptor characters
(character-reg registers
:locations #.non-descriptor-regs
:constant-scs (immediate)
:save-p t
:alternate-scs (character-stack))
;; Non-Descriptor SAP's (arbitrary pointers into address space)
(sap-reg registers
:locations #.non-descriptor-regs
:constant-scs (immediate)
:save-p t
:alternate-scs (sap-stack))
;; Non-Descriptor (signed or unsigned) numbers.
(signed-reg registers
:locations #.non-descriptor-regs
:constant-scs (immediate)
:save-p t
:alternate-scs (signed-stack))
(unsigned-reg registers
:locations #.non-descriptor-regs
:constant-scs (immediate)
:save-p t
:alternate-scs (unsigned-stack))
;; Random objects that must not be seen by GC. Used only as temporaries.
(non-descriptor-reg registers
:locations #.non-descriptor-regs)
;; Pointers to the interior of objects. Used only as a temporary.
(interior-reg registers
:locations (#.lr-offset))
;; **** Things that can go in the floating point registers.
;; Non-Descriptor single-floats.
(single-reg float-registers
:locations #.(loop for i below 32 collect i)
:constant-scs ()
:save-p t
:alternate-scs (single-stack))
;; Non-Descriptor double-floats.
(double-reg float-registers
:locations #.(loop for i below 32 by 2 collect i)
:element-size 2
:constant-scs ()
:save-p t
:alternate-scs (double-stack))
(complex-single-reg float-registers
:locations #.(loop for i from 0 below 32 by 2 collect i)
:element-size 2
:constant-scs ()
:save-p t
:alternate-scs (complex-single-stack))
(complex-double-reg float-registers
:locations #.(loop for i from 0 below 32 by 4 collect i)
:element-size 4
:constant-scs ()
:save-p t
:alternate-scs (complex-double-stack))
;; A catch or unwind block.
(catch-block control-stack :element-size catch-block-size))
;;;; Make some random tns for important registers.
(macrolet ((defregtn (name sc)
(let ((offset-sym (symbolicate name "-OFFSET"))
(tn-sym (symbolicate name "-TN")))
`(defparameter ,tn-sym
(make-random-tn :kind :normal
:sc (sc-or-lose ',sc)
:offset ,offset-sym)))))
(defregtn null descriptor-reg)
(defregtn code descriptor-reg)
(defregtn nargs any-reg)
(defregtn ocfp any-reg)
(defregtn nsp any-reg)
(defregtn cfp any-reg)
(defregtn lr interior-reg)
(defregtn pc any-reg))
;;; If VALUE can be represented as an immediate constant, then return the
;;; appropriate SC number, otherwise return NIL.
(defun immediate-constant-sc (value)
(typecase value
(null
(sc-number-or-lose 'null))
((or (integer #.sb!xc:most-negative-fixnum #.sb!xc:most-positive-fixnum)
character)
(sc-number-or-lose 'immediate))
(symbol
(if (static-symbol-p value)
(sc-number-or-lose 'immediate)
nil))))
(defun boxed-immediate-sc-p (sc)
(or (eql sc (sc-number-or-lose 'null))
(eql sc (sc-number-or-lose 'immediate))))
;;;; function call parameters
;;; the SC numbers for register and stack arguments/return values
(def!constant register-arg-scn (sc-number-or-lose 'descriptor-reg))
(def!constant immediate-arg-scn (sc-number-or-lose 'any-reg))
(def!constant control-stack-arg-scn (sc-number-or-lose 'control-stack))
;;; offsets of special stack frame locations
(def!constant ocfp-save-offset 0)
(def!constant lra-save-offset 1)
(def!constant nfp-save-offset 2)
;;; This is used by the debugger.
;;; < nyef> Ah, right. So, SINGLE-VALUE-RETURN-BYTE-OFFSET doesn't apply to x86oids or ARM.
(def!constant single-value-return-byte-offset 0)
;;; A list of TN's describing the register arguments.
;;;
(defparameter *register-arg-tns*
(mapcar #'(lambda (n)
(make-random-tn :kind :normal
:sc (sc-or-lose 'descriptor-reg)
:offset n))
*register-arg-offsets*))
;;; This function is called by debug output routines that want a pretty name
;;; for a TN's location. It returns a thing that can be printed with PRINC.
(defun location-print-name (tn)
(declare (type tn tn))
(let ((sb (sb-name (sc-sb (tn-sc tn))))
(offset (tn-offset tn)))
(ecase sb
(registers (or (svref *register-names* offset)
(format nil "R~D" offset)))
(control-stack (format nil "CS~D" offset))
(non-descriptor-stack (format nil "NS~D" offset))
(constant (format nil "Const~D" offset))
(immediate-constant "Immed")
(float-registers (format nil "F~D" offset)))))
(defun combination-implementation-style (node)
(flet ((valid-funtype (args result)
(sb!c::valid-fun-use node
(sb!c::specifier-type
`(function ,args ,result)))))
(case (sb!c::combination-fun-source-name node)
(logtest
(cond
((valid-funtype '(fixnum fixnum) '*)
(values :maybe nil))
((valid-funtype '((signed-byte 32) (signed-byte 32)) '*)
(values :maybe nil))
((valid-funtype '((unsigned-byte 32) (unsigned-byte 32)) '*)
(values :maybe nil))
(t (values :default nil))))
(logbitp
(cond
((or (valid-funtype '((constant-arg (integer 0 29)) fixnum) '*)
(valid-funtype '((constant-arg (integer 0 31)) (signed-byte 32)) '*)
(valid-funtype '((constant-arg (integer 0 31)) (unsigned-byte 32)) '*))
(values :transform '(lambda (index integer)
(%logbitp integer index))))
(t (values :default nil))))
(t (values :default nil)))))
(defun primitive-type-indirect-cell-type (ptype)
(declare (ignore ptype))
nil)

View file

@ -87,7 +87,7 @@
(movable foldable flushable commutative))
(defknown (equal equalp) (t t) boolean (foldable flushable recursive))
#!+(or x86 x86-64 arm)
#!+(or x86 x86-64 arm arm64)
(defknown fixnum-mod-p (t fixnum) boolean
(movable foldable flushable always-translatable))

View file

@ -641,7 +641,7 @@
;; don't have a true Alpha64 port yet, we'll have to stick to
;; SB!VM:N-MACHINE-WORD-BITS for the time being. --njf, 2004-08-14
#.`(progn
#!+(or x86 x86-64 arm)
#!+(or x86 x86-64 arm arm64)
(def sb!vm::ash-left-modfx
:tagged ,(- sb!vm:n-word-bits sb!vm:n-fixnum-tag-bits) t)
(def ,(intern (format nil "ASH-LEFT-MOD~D" sb!vm:n-machine-word-bits)

View file

@ -0,0 +1,615 @@
;;;; type testing and checking VOPs for the x86-64 VM
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB!VM")
;;;; test generation utilities
;;; Optimize the case of moving a 64-bit value into RAX when not caring
;;; about the upper 32 bits: often the REX prefix can be spared.
(defun move-qword-to-eax (value)
(if (and (sc-is value any-reg descriptor-reg)
(< (tn-offset value) r8-offset))
(move eax-tn (reg-in-size value :dword))
(move rax-tn value)))
(defun generate-fixnum-test (value)
#!+sb-doc
"Set the Z flag if VALUE is fixnum"
(inst test
(cond ((sc-is value any-reg descriptor-reg)
(reg-in-size value :byte))
;; This is hooey. None of the type-vops presently allow
;; control-stack as a storage class.
((sc-is value control-stack)
(make-ea :byte :base rbp-tn
:disp (frame-byte-offset (tn-offset value))))
(t
value))
fixnum-tag-mask))
(defun %test-fixnum (value target not-p)
(generate-fixnum-test value)
(inst jmp (if not-p :nz :z) target))
;;; General FIXME: it's fine that we wire these to use rAX which has
;;; the shortest encoding, but for goodness sake can we pass the TN
;;; from the VOP like every other backend does? Freely referencing the
;;; permanent globals RAX-TN,EAX-TN,AL-TN is a bad way to go about it.
(defun %lea-for-lowtag-test (target value lowtag)
(inst lea target (make-ea :dword :base value :disp (- lowtag))))
;; Numerics including fixnum, excluding short-float. (INTEGER,RATIONAL)
(defun %test-fixnum-and-headers (value target not-p headers)
(let ((drop-through (gen-label)))
(case n-fixnum-tag-bits
(1 (%lea-for-lowtag-test eax-tn value other-pointer-lowtag)
(inst test al-tn 1)
(inst jmp :nz (if not-p drop-through target)) ; inverted
(%test-headers value target not-p nil headers
:drop-through drop-through :compute-eax nil))
(t
(generate-fixnum-test value)
(inst jmp :z (if not-p drop-through target))
(%test-headers value target not-p nil headers
:drop-through drop-through)))))
;; I can see no reason this would ever be used.
;; (or fixnum character|unbound-marker) is implausible.
(defun %test-fixnum-and-immediate (value target not-p immediate)
(let ((drop-through (gen-label)))
(generate-fixnum-test value)
(inst jmp :z (if not-p drop-through target))
(%test-immediate value target not-p immediate drop-through)))
;; Numerics
(defun %test-fixnum-immediate-and-headers (value target not-p immediate
headers)
(let ((drop-through (gen-label)))
(case n-fixnum-tag-bits
(1 (%lea-for-lowtag-test eax-tn value other-pointer-lowtag)
(inst test al-tn 1)
(inst jmp :nz (if not-p drop-through target)) ; inverted
(inst cmp al-tn (- immediate other-pointer-lowtag))
(inst jmp :e (if not-p drop-through target))
(%test-headers value target not-p nil headers
:drop-through drop-through :compute-eax nil))
(t (generate-fixnum-test value)
(inst jmp :z (if not-p drop-through target))
(%test-immediate-and-headers value target not-p immediate headers
drop-through)))))
(defun %test-immediate (value target not-p immediate
&optional (drop-through (gen-label)))
;; Code a single instruction byte test if possible.
(cond ((sc-is value any-reg descriptor-reg)
(inst cmp (reg-in-size value :byte) immediate))
(t
(move rax-tn value)
(inst cmp al-tn immediate)))
(inst jmp (if not-p :ne :e) target)
(emit-label drop-through))
;; Numerics including short-float, excluding fixnum
(defun %test-immediate-and-headers (value target not-p immediate headers
&optional (drop-through (gen-label)))
;; Code a single instruction byte test if possible.
(cond ((sc-is value any-reg descriptor-reg)
(inst cmp (reg-in-size value :byte) immediate))
(t
(move rax-tn value)
(inst cmp al-tn immediate)))
(inst jmp :e (if not-p drop-through target))
(%test-headers value target not-p nil headers :drop-through drop-through))
(defun %test-lowtag (value target not-p lowtag)
(%lea-for-lowtag-test eax-tn value lowtag)
(inst test al-tn lowtag-mask)
(inst jmp (if not-p :nz :z) target))
(defun %test-headers (value target not-p function-p headers
&key except
(drop-through (gen-label))
(compute-eax t))
(let ((lowtag (if function-p fun-pointer-lowtag other-pointer-lowtag)))
(multiple-value-bind (equal less-or-equal greater-or-equal when-true
when-false)
;; EQUAL, LESS-OR-EQUAL, and GREATER-OR-EQUAL are the conditions
;; for branching to TARGET. WHEN-TRUE and WHEN-FALSE are the
;; labels to branch to when we know it's true and when we know
;; it's false respectively.
(if not-p
(values :ne :a :b drop-through target)
(values :e :na :nb target drop-through))
(when compute-eax
(%lea-for-lowtag-test eax-tn value lowtag))
(inst test al-tn lowtag-mask)
(inst jmp :nz when-false)
;; FIXME: this backend seems to be missing the special logic for
;; testing exactly two widetags differing only in a single bit,
;; which through evolution is almost totally unworkable anyway...
(do ((remaining headers (cdr remaining))
;; It is preferable (smaller and faster code) to directly
;; compare the value in memory instead of loading it into
;; a register first. Find out if this is possible and set
;; WIDETAG-TN accordingly. If impossible, generate the
;; register load.
;; Compared to x86 we additionally optimize the cases of a
;; range starting with BIGNUM-WIDETAG (= min widetag)
;; or ending with COMPLEX-ARRAY-WIDETAG (= max widetag)
(widetag-tn (if (and (null (cdr headers))
(not except)
(or (atom (car headers))
(= (caar headers) bignum-widetag)
(= (cdar headers) complex-array-widetag)))
(make-ea :byte :base value :disp (- lowtag))
(progn
(inst mov eax-tn (make-ea :dword :base value
:disp (- lowtag)))
al-tn))))
((null remaining))
(dolist (widetag except) ; only after loading widetag-tn
(inst cmp al-tn widetag)
(inst jmp :e when-false))
(setq except nil)
(let ((header (car remaining))
(last (null (cdr remaining))))
(cond
((atom header)
(inst cmp widetag-tn header)
(if last
(inst jmp equal target)
(inst jmp :e when-true)))
(t
(let ((start (car header))
(end (cdr header)))
(cond
((= start bignum-widetag)
(inst cmp widetag-tn end)
(if last
(inst jmp less-or-equal target)
(inst jmp :be when-true)))
((= end complex-array-widetag)
(inst cmp widetag-tn start)
(if last
(inst jmp greater-or-equal target)
(inst jmp :b when-false)))
((not last)
(inst cmp al-tn start)
(inst jmp :b when-false)
(inst cmp al-tn end)
(inst jmp :be when-true))
(t
(inst sub al-tn start)
(inst cmp al-tn (- end start))
(inst jmp less-or-equal target))))))))
(emit-label drop-through))))
;;;; type checking and testing
(define-vop (check-type)
(:args (value :target result :scs (any-reg descriptor-reg)))
(:results (result :scs (any-reg descriptor-reg)))
(:temporary (:sc unsigned-reg :offset eax-offset :to (:result 0)) eax)
(:ignore eax)
(:vop-var vop)
(:save-p :compute-only))
(define-vop (type-predicate)
(:args (value :scs (any-reg descriptor-reg)))
(:temporary (:sc unsigned-reg :offset eax-offset) eax)
(:ignore eax)
(:conditional)
(:info target not-p)
(:policy :fast-safe))
;;; simpler VOP that don't need a temporary register
(define-vop (simple-check-type)
(:args (value :target result :scs (any-reg descriptor-reg)))
(:results (result :scs (any-reg descriptor-reg)
:load-if (not (and (sc-is value any-reg descriptor-reg)
(sc-is result control-stack)))))
(:vop-var vop)
(:save-p :compute-only))
(define-vop (simple-type-predicate)
(:args (value :scs (any-reg descriptor-reg control-stack)))
(:conditional)
(:info target not-p)
(:policy :fast-safe))
(defmacro !define-type-vops (pred-name check-name ptype error-code
(&rest type-codes)
&key (variant nil variant-p) &allow-other-keys)
;; KLUDGE: UGH. Why do we need this eval? Can't we put this in the
;; expansion?
(flet ((cost-to-test-types (type-codes)
(+ (* 2 (length type-codes))
(if (> (apply #'max type-codes) lowtag-limit) 7 2))))
(let* ((cost (cost-to-test-types (mapcar #'eval type-codes)))
(prefix (if variant-p
(concatenate 'string (string variant) "-")
"")))
`(progn
,@(when pred-name
`((define-vop (,pred-name ,(intern (concatenate 'string prefix "TYPE-PREDICATE")))
(:translate ,pred-name)
(:generator ,cost
(test-type value target not-p (,@type-codes))))))
,@(when check-name
`((define-vop (,check-name ,(intern (concatenate 'string prefix "CHECK-TYPE")))
(:generator ,cost
(let ((err-lab
(generate-error-code vop ',error-code value)))
(test-type value err-lab t (,@type-codes))
(move result value))))))
,@(when ptype
`((primitive-type-vop ,check-name (:check) ,ptype)))))))
;;;; other integer ranges
(define-vop (fixnump/unsigned-byte-64 simple-type-predicate)
(:args (value :scs (unsigned-reg)))
(:arg-types unsigned-num)
(:translate fixnump)
(:temporary (:sc unsigned-reg :from (:argument 0)) tmp)
(:info)
(:conditional :z)
(:generator 5
(move tmp value)
(inst shr tmp n-positive-fixnum-bits)))
#-#.(cl:if (cl:= sb!vm:n-fixnum-tag-bits 1) '(:and) '(:or))
(define-vop (fixnump/signed-byte-64 simple-type-predicate)
(:args (value :scs (signed-reg)))
(:info)
(:conditional :z)
(:temporary (:sc unsigned-reg) temp)
(:arg-types signed-num)
(:translate fixnump)
(:generator 5
;; Hackers Delight, p. 53: signed
;; a <= x <= a + 2^n - 1
;; is equivalent to unsigned
;; ((x-a) >> n) = 0
(inst mov temp #.(- sb!xc:most-negative-fixnum))
(inst add temp value)
(inst shr temp n-fixnum-bits)))
#+#.(cl:if (cl:= sb!vm:n-fixnum-tag-bits 1) '(:and) '(:or))
(define-vop (fixnump/signed-byte-64 simple-type-predicate)
(:args (value :scs (signed-reg) :target temp))
(:info)
(:conditional :no)
(:temporary (:sc unsigned-reg :from (:argument 0)) temp)
(:arg-types signed-num)
(:translate fixnump)
(:generator 5
(move temp value)
;; The overflow flag will be set if the reg's sign bit changes.
(inst shl temp 1)))
;;; A (SIGNED-BYTE 64) can be represented with either fixnum or a bignum with
;;; exactly one digit.
(define-vop (signed-byte-64-p type-predicate)
(:translate signed-byte-64-p)
(:generator 45
(multiple-value-bind (yep nope)
(if not-p
(values not-target target)
(values target not-target))
#.(case n-fixnum-tag-bits
(1 '(progn
(%lea-for-lowtag-test eax-tn value other-pointer-lowtag)
(inst test al-tn fixnum-tag-mask) ; 0th bit = 1 => fixnum
(inst jmp :nz yep)
(inst test al-tn lowtag-mask)))
(t '(progn
(move-qword-to-eax value)
(inst test al-tn fixnum-tag-mask)
(inst jmp :e yep)
(inst and al-tn lowtag-mask)
(inst cmp al-tn other-pointer-lowtag))))
(inst jmp :ne nope)
(inst cmp (make-ea-for-object-slot value 0 other-pointer-lowtag)
(+ (ash 1 n-widetag-bits) bignum-widetag))
(inst jmp (if not-p :ne :e) target))
NOT-TARGET))
;; FIXME: this vop is never emitted. I suspect that is because whenever
;; we have something which needs to be asserted as (SIGNED-BYTE 64) and
;; then moved to a signed-reg, the signed-byte-64-p vop is used and then
;; move-to-word. We apparently never want to both assert the type and
;; keep it as a tagged object. Anyway if it ever were emitted before,
;; GENERATE-ERROR-CODE would have failed since OBJECT-NOT-SIGNED-BYTE-64
;; did not have an error number.
(define-vop (check-signed-byte-64 check-type)
(:generator 45
(let ((nope (generate-error-code vop
'object-not-signed-byte-64-error
value)))
(generate-fixnum-test value)
(inst jmp :e yep)
(move-qword-to-eax value)
(inst and al-tn lowtag-mask)
(inst cmp al-tn other-pointer-lowtag)
(inst jmp :ne nope)
(inst cmp (make-ea-for-object-slot value 0 other-pointer-lowtag)
(+ (ash 1 n-widetag-bits) bignum-widetag))
(inst jmp :ne nope))
YEP
(move result value)))
;;; An (unsigned-byte 64) can be represented with either a positive
;;; fixnum, a bignum with exactly one positive digit, or a bignum with
;;; exactly two digits and the second digit all zeros.
(define-vop (unsigned-byte-64-p type-predicate)
(:translate unsigned-byte-64-p)
(:generator 45
(let ((not-target (gen-label))
(single-word (gen-label))
(fixnum (gen-label)))
(multiple-value-bind (yep nope)
(if not-p
(values not-target target)
(values target not-target))
;; Is it a fixnum?
(move rax-tn value)
(inst test al-tn fixnum-tag-mask)
(inst jmp :e fixnum)
;; If not, is it an other pointer?
(inst and al-tn lowtag-mask)
(inst cmp al-tn other-pointer-lowtag)
(inst jmp :ne nope)
;; Get the header.
(loadw rax-tn value 0 other-pointer-lowtag)
;; Is it one?
(inst cmp rax-tn (+ (ash 1 n-widetag-bits) bignum-widetag))
(inst jmp :e single-word)
;; If it's other than two, we can't be an (unsigned-byte 64)
(inst cmp rax-tn (+ (ash 2 n-widetag-bits) bignum-widetag))
(inst jmp :ne nope)
;; Get the second digit.
(loadw rax-tn value (1+ bignum-digits-offset) other-pointer-lowtag)
;; All zeros, its an (unsigned-byte 64).
(inst test rax-tn rax-tn)
(inst jmp :z yep)
(inst jmp nope)
(emit-label single-word)
;; Get the single digit.
(loadw rax-tn value bignum-digits-offset other-pointer-lowtag)
;; positive implies (unsigned-byte 64).
(emit-label fixnum)
(inst test rax-tn rax-tn)
(inst jmp (if not-p :s :ns) target)
(emit-label not-target)))))
(define-vop (check-unsigned-byte-64 check-type)
(:generator 45
(let ((nope
(generate-error-code vop 'object-not-unsigned-byte-64-error value))
(yep (gen-label))
(fixnum (gen-label))
(single-word (gen-label)))
;; Is it a fixnum?
(generate-fixnum-test value)
(move rax-tn value)
(inst jmp :e fixnum)
;; If not, is it an other pointer?
(inst and al-tn lowtag-mask)
(inst cmp al-tn other-pointer-lowtag)
(inst jmp :ne nope)
;; Get the header.
(loadw rax-tn value 0 other-pointer-lowtag)
;; Is it one?
(inst cmp rax-tn (+ (ash 1 n-widetag-bits) bignum-widetag))
(inst jmp :e single-word)
;; If it's other than two, we can't be an (unsigned-byte 64)
(inst cmp rax-tn (+ (ash 2 n-widetag-bits) bignum-widetag))
(inst jmp :ne nope)
;; Get the second digit.
(loadw rax-tn value (1+ bignum-digits-offset) other-pointer-lowtag)
;; All zeros, its an (unsigned-byte 64).
(inst test rax-tn rax-tn)
(inst jmp :z yep)
(inst jmp nope)
(emit-label single-word)
;; Get the single digit.
(loadw rax-tn value bignum-digits-offset other-pointer-lowtag)
;; positive implies (unsigned-byte 64).
(emit-label fixnum)
(inst test rax-tn rax-tn)
(inst jmp :s nope)
(emit-label yep)
(move result value))))
(defun power-of-two-limit-p (x)
(and (fixnump x)
(= (logcount (1+ x)) 1)))
(define-vop (test-fixnum-mod-power-of-two)
(:args (value :scs (any-reg descriptor-reg
unsigned-reg signed-reg
immediate)))
(:arg-types *
(:constant (satisfies power-of-two-limit-p)))
(:translate fixnum-mod-p)
(:conditional :e)
(:info hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 4
(aver (not (sc-is value immediate)))
(let* ((fixnum-hi (if (sc-is value unsigned-reg signed-reg)
hi
(fixnumize hi))))
(inst test value (constantize (lognot fixnum-hi))))))
(define-vop (test-fixnum-mod-tagged-unsigned)
(:args (value :scs (any-reg descriptor-reg
unsigned-reg signed-reg
immediate)))
(:arg-types (:or tagged-num unsigned-num signed-num)
(:constant fixnum))
(:translate fixnum-mod-p)
(:conditional :be)
(:info hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 5
(aver (not (sc-is value immediate)))
(let ((fixnum-hi (if (sc-is value unsigned-reg signed-reg)
hi
(fixnumize hi))))
(inst cmp value (constantize fixnum-hi)))))
(define-vop (test-fixnum-mod-*)
(:args (value :scs (any-reg descriptor-reg)))
(:arg-types * (:constant fixnum))
(:translate fixnum-mod-p)
(:conditional)
(:info target not-p hi)
(:save-p :compute-only)
(:policy :fast-safe)
(:generator 6
(let* ((fixnum-hi (fixnumize hi))
(skip (gen-label)))
(generate-fixnum-test value)
(inst jmp :ne (if not-p target skip))
(inst cmp value (constantize fixnum-hi))
(inst jmp (if not-p :a :be) target)
(emit-label skip))))
;;;; list/symbol types
;;;
;;; symbolp (or symbol (eq nil))
;;; consp (and list (not (eq nil)))
(define-vop (symbolp type-predicate)
(:translate symbolp)
(:generator 12
(let ((is-symbol-label (if not-p DROP-THRU target)))
(inst cmp value nil-value)
(inst jmp :e is-symbol-label)
(test-type value target not-p (symbol-header-widetag)))
DROP-THRU))
(define-vop (check-symbol check-type)
(:generator 12
(let ((error (generate-error-code vop 'object-not-symbol-error value)))
(inst cmp value nil-value)
(inst jmp :e DROP-THRU)
(test-type value error t (symbol-header-widetag)))
DROP-THRU
(move result value)))
(define-vop (consp type-predicate)
(:translate consp)
(:generator 8
(let ((is-not-cons-label (if not-p target DROP-THRU)))
(inst cmp value nil-value)
(inst jmp :e is-not-cons-label)
(test-type value target not-p (list-pointer-lowtag)))
DROP-THRU))
(define-vop (check-cons check-type)
(:generator 8
(let ((error (generate-error-code vop 'object-not-cons-error value)))
(inst cmp value nil-value)
(inst jmp :e error)
(test-type value error t (list-pointer-lowtag))
(move result value))))
;; A vop that accepts a computed set of widetags.
(define-vop (%other-pointer-subtype-p type-predicate)
(:translate %other-pointer-subtype-p)
(:info target not-p widetags)
(:arg-types * (:constant t)) ; voodoo - 'target' and 'not-p' are absent
(:generator 15 ; arbitrary
(multiple-value-bind (headers exceptions)
(canonicalize-headers-and-exceptions widetags)
(%test-headers value target not-p nil headers
:except exceptions))))
#!+sb-simd-pack
(progn
(!define-type-vops simd-pack-p nil nil nil (simd-pack-widetag))
(define-vop (check-simd-pack check-type)
(:args (value :target result
:scs (any-reg descriptor-reg
int-sse-reg single-sse-reg double-sse-reg
int-sse-stack single-sse-stack double-sse-stack)))
(:results (result :scs (any-reg descriptor-reg
int-sse-reg single-sse-reg double-sse-reg)))
(:temporary (:sc unsigned-reg :offset eax-offset :to (:result 0)) eax)
(:ignore eax)
(:vop-var vop)
(:node-var node)
(:save-p :compute-only)
(:generator 50
(sc-case value
((int-sse-reg single-sse-reg double-sse-reg
int-sse-stack single-sse-stack double-sse-stack)
(sc-case result
((int-sse-reg single-sse-reg double-sse-reg)
(move result value))
((any-reg descriptor-reg)
(with-fixed-allocation (result
simd-pack-widetag
simd-pack-size
node)
;; see *simd-pack-element-types*
(storew (fixnumize
(sc-case value
((int-sse-reg int-sse-stack) 0)
((single-sse-reg single-sse-stack) 1)
((double-sse-reg double-sse-stack) 2)))
result simd-pack-tag-slot other-pointer-lowtag)
(let ((ea (make-ea-for-object-slot
result simd-pack-lo-value-slot other-pointer-lowtag)))
(if (float-simd-pack-p value)
(inst movaps ea value)
(inst movdqa ea value)))))))
((any-reg descriptor-reg)
(let ((leaf (sb!c::tn-leaf value)))
(unless (and (sb!c::lvar-p leaf)
(csubtypep (sb!c::lvar-type leaf)
(specifier-type 'simd-pack)))
(test-type
value
(generate-error-code vop 'object-not-simd-pack-error value)
t (simd-pack-widetag))))
(sc-case result
((int-sse-reg)
(let ((ea (make-ea-for-object-slot
value simd-pack-lo-value-slot other-pointer-lowtag)))
(inst movdqa result ea)))
((single-sse-reg double-sse-reg)
(let ((ea (make-ea-for-object-slot
value simd-pack-lo-value-slot other-pointer-lowtag)))
(inst movaps result ea)))
((any-reg descriptor-reg)
(move result value)))))))
(primitive-type-vop check-simd-pack (:check) simd-pack-int simd-pack-single simd-pack-double))

View file

@ -0,0 +1 @@
stas@debian.6597:1440612832

View file

@ -0,0 +1,40 @@
# -*- makefile -*- for the C-level run-time support for SBCL
# This software is part of the SBCL system. See the README file for
# more information.
#
# This software is derived from the CMU CL system, which was
# written at Carnegie Mellon University and released into the
# public domain. The software is in the public domain and is
# provided with absolutely no warranty. See the COPYING and CREDITS
# files for more information.
CFLAGS = -g
#LINKFLAGS += -v -rdynamic -m32
NM = ./linux-nm
ASSEM_SRC = arm64-assem.S ldso-stubs.S
ARCH_SRC = arm64-arch.c
OS_SRC = linux-os.c arm64-linux-os.c
OS_LIBS = -ldl
ifdef LISP_FEATURE_GENCGC
GC_SRC = gencgc.c
else
GC_SRC = cheneygc.c
endif
ifdef LISP_FEATURE_SB_THREAD
OS_LIBS += -lpthread
endif
ifdef LISP_FEATURE_SB_CORE_COMPRESSION
OS_LIBS += -lz
endif
ifdef LISP_FEATURE_LARGEFILE
CFLAGS += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
endif
# Nothing to do for after-grovel-headers.
.PHONY: after-grovel-headers
after-grovel-headers:

0
src/runtime/a.out Normal file
View file

200
src/runtime/arm64-arch.c Normal file
View file

@ -0,0 +1,200 @@
/*
* This software is part of the SBCL system. See the README file for
* more information.
*
* This software is derived from the CMU CL system, which was
* written at Carnegie Mellon University and released into the
* public domain. The software is in the public domain and is
* provided with absolutely no warranty. See the COPYING and CREDITS
* files for more information.
*/
#include <stdio.h>
#include "sbcl.h"
#include "runtime.h"
#include "arch.h"
#include "globals.h"
#include "validate.h"
#include "os.h"
#include "lispregs.h"
#include "signal.h"
#include "alloc.h"
#include "interrupt.h"
#include "interr.h"
#include "breakpoint.h"
#include "monitor.h"
void arch_init(void)
{
return;
}
os_vm_address_t arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
{
return (os_vm_address_t)code->si_addr;
}
void arch_skip_instruction(os_context_t *context)
{
/* KLUDGE: Other platforms check for trap codes and skip inlined
* trap/error parameters. We should too. */
/* Note that we're doing integer arithmetic here, not pointer. So
* the value that the return value of os_context_pc_addr() points
* to will be incremented by 4, not 16.
*/
*os_context_pc_addr(context) += 4;
}
unsigned char *arch_internal_error_arguments(os_context_t *context)
{
return (unsigned char *)(*os_context_pc_addr(context) + 5);
}
boolean arch_pseudo_atomic_atomic(os_context_t *context)
{
/* FIXME: this foreign_function_call_active test is dubious at
* best. If a foreign call is made in a pseudo atomic section
* (?) or more likely a pseudo atomic section is in a foreign
* call then an interrupt is executed immediately. Maybe it
* has to do with C code not maintaining pseudo atomic
* properly. MG - 2005-08-10
*
* The foreign_function_call_active used to live at each call-site
* to arch_pseudo_atomic_atomic, but this seems clearer.
* --NS 2007-05-15 */
#ifdef LISP_FEATURE_GENCGC
return SymbolValue(PSEUDO_ATOMIC_ATOMIC, 0) != NIL;
#else
return (!foreign_function_call_active)
&& (NIL != SymbolValue(PSEUDO_ATOMIC_ATOMIC,0));
#endif
}
void arch_set_pseudo_atomic_interrupted(os_context_t *context)
{
SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED, do_pending_interrupt, 0);
}
void arch_clear_pseudo_atomic_interrupted(os_context_t *context)
{
SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED, 0, 0);
}
unsigned int arch_install_breakpoint(void *pc)
{
/* FIXME: Implement. */
return 0;
}
void arch_remove_breakpoint(void *pc, unsigned int orig_inst)
{
/* FIXME: Implement. */
}
void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
{
/* FIXME: Implement. */
}
void
arch_handle_breakpoint(os_context_t *context)
{
handle_breakpoint(context);
}
void
arch_handle_fun_end_breakpoint(os_context_t *context)
{
*os_context_pc_addr(context) = (int) handle_fun_end_breakpoint(context);
}
void
arch_handle_single_step_trap(os_context_t *context, int trap)
{
unsigned char register_offset =
*((unsigned char *)(*os_context_pc_addr(context))+5);
handle_single_step_trap(context, trap, register_offset);
/* KLUDGE: arch_skip_instruction() only skips one instruction, and
* there is a following word to deal with as well, so skip
* twice. */
arch_skip_instruction(context);
arch_skip_instruction(context);
}
static void
sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
{
unsigned int code = *((unsigned char *)(4+*os_context_pc_addr(context)));
u32 trap_instruction = *((u32 *)*os_context_pc_addr(context));
if (trap_instruction != 0xe7f001f0) {
lose("Unrecognized trap instruction %08lx in sigtrap_handler()",
trap_instruction);
}
if (code == trap_PendingInterrupt) {
arch_skip_instruction(context);
}
handle_trap(context, code);
}
void arch_install_interrupt_handlers()
{
undoably_install_low_level_interrupt_handler(SIGTRAP, sigtrap_handler);
}
#ifdef LISP_FEATURE_LINKAGE_TABLE
/* Linkage tables
*
* Linkage entry size is 16, because we need 4 instructions.
*/
#define LINKAGE_TEMP_REG reg_NFP
void arch_write_linkage_table_jmp(void* reloc_addr, void *target_addr)
{
/*
ldr reg, [pc, #4]
bx reg
nop
address
BX is needed for thumb interworking, without it it could take just two words with
ldr pc, [pc, #-4]
address
*/
int* inst_ptr;
unsigned inst;
inst_ptr = (int*) reloc_addr;
// ldr reg, [pc, #4]
inst = 0xe59f0000 | LINKAGE_TEMP_REG << 12 | 4;
*inst_ptr++ = inst;
// bx reg
inst = 0xe12fff10 | LINKAGE_TEMP_REG;
*inst_ptr++ = inst;
// nop aka mov r0, r0
inst = 0xe1a00000;
*inst_ptr++ = inst;
// address
*inst_ptr++ = target_addr;
os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - (char*) reloc_addr);
}
void
arch_write_linkage_table_ref(void * reloc_addr, void *target_addr)
{
*(unsigned long *)reloc_addr = (unsigned long)target_addr;
}
#endif

36
src/runtime/arm64-arch.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef _ARM_ARCH_H
#define _ARM_ARCH_H
#define ALIEN_STACK_GROWS_DOWNWARD
static inline long
get_spinlock(lispobj *word,long value)
{
#ifdef LISP_FEATURE_SB_THREAD
#error "get_spinlock not defined for threads"
#else
*word=value;
return 0;
#endif
}
static inline void
release_spinlock(lispobj *word)
{
#ifdef LISP_FEATURE_SB_THREAD
#error "release_spinlock not defined for threads"
#endif
*word=0;
}
#ifdef LISP_FEATURE_SB_THREAD
static inline lispobj
swap_lispobjs(volatile lispobj *dest, lispobj value)
{
#error "swap_lispobjs not defined for threads"
}
#endif
#define ARCH_HAS_LINK_REGISTER
#endif /* _ARM_ARCH_H */

480
src/runtime/arm64-assem.S Normal file
View file

@ -0,0 +1,480 @@
#define LANGUAGE_ASSEMBLY
#include "lispregs.h"
#include "globals.h"
#include "sbcl.h"
#include "genesis/closure.h"
#include "genesis/funcallable-instance.h"
#include "genesis/fdefn.h"
#include "genesis/static-symbols.h"
#include "genesis/simple-fun.h"
#include "genesis/symbol.h"
#define STATIC_SYMBOL_VALUE(sym) [reg_NULL, #(((sym)-NIL)+SYMBOL_VALUE_OFFSET)]
.align
.global call_into_lisp
.type call_into_lisp, %function
call_into_lisp:
// At this point, we have:
// X0 - function
// X1 - pointer to args
// X2 - number of args (unboxed)
// There will be no more than three args, so we don't need to
// worry about parameters to be passed on the stack.
// All registers other than X0-X3 and X12 are callee-saves.
// Save X3 to get 8-byte alignemnt.
stmfd sp!, {x3-x11, lr}
fstmfdd sp!, {d8-d15}
// Start by finding NIL.
ldr reg_NULL, .known_nil
// Set up NARGS.
mov reg_NARGS, x2, lsl #2
// Move args pointer out of the way of the args to be loaded.
mov reg_X8, x1
// Move the function to its passing location.
mov reg_LEXENV, x0
// Clear the boxed registers that don't already have something
// in them.
mov reg_CODE, #0
mov reg_X2, #0
// Find the lisp stack and frame pointers. We're allocating a
// new lisp stack frame, so load the stack pointer into CFP.
// And we need the frame pointer, but OCFP is in use, so use
// NFP instead.
ldr reg_NFP, .frame_pointer_address
ldr reg_CFP, .stack_pointer_address
ldr reg_NFP, [reg_NFP]
ldr reg_CFP, [reg_CFP]
// Enter PSEUDO-ATOMIC.
str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
#ifndef LISP_FEATURE_GENCGC
// Copy the current allocation pointer into the symbol.
ldr reg_OCFP, =dynamic_space_free_pointer
ldr reg_OCFP, [reg_OCFP]
str reg_OCFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
#endif
// Clear FFCA, so the runtime knows that we're "in lisp".
ldr reg_OCFP, =foreign_function_call_active
str reg_X2, [reg_OCFP]
// We need to set up the lisp stack pointer and the basics of
// our stack frame while we're still in P-A. Any sooner and
// our stack frame can be clobbered by a stray interrupt, any
// later and we can end up with a half-configured stack frame
// when we catch a stray interrupt.
// Allocate our frame and set up the Lisp stack pointer
add reg_OCFP, reg_CFP, #8
str reg_OCFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
// Set up the "frame link"
str reg_NFP, [reg_OCFP, #-8]
// Set up the return address
ldr reg_NL3, =.lra
str reg_NL3, [reg_OCFP, #-4]
// Leave PSEUDO-ATOMIC and check for interrupts.
str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
cmp reg_OCFP, #0
blxne reg_OCFP
// Load our function args. Cleverness abounds!
rsb reg_NL3, reg_NARGS, #8
add pc, pc, reg_NL3
ldr reg_X2, [reg_X8, #8]
ldr reg_X1, [reg_X8, #4]
ldr reg_X0, [reg_X8]
// Load the closure-fun (or simple-fun-self), in case we're
// trying to call a closure.
ldr reg_CODE, [reg_LEXENV, #CLOSURE_FUN_OFFSET]
// And, finally, call into Lisp!
add reg_PC, reg_CODE, #SIMPLE_FUN_CODE_OFFSET
.align 3
.equ .lra, .+OTHER_POINTER_LOWTAG
.word RETURN_PC_HEADER_WIDETAG
// Correct stack pointer for return processing.
streq reg_OCFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
// Enter PSEUDO-ATOMIC.
str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
// Save the lisp stack and frame pointers.
ldr reg_NFP, .frame_pointer_address
str reg_CFP, [reg_NFP]
ldr reg_NFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
ldr reg_OCFP, .stack_pointer_address
str reg_NFP, [reg_OCFP]
// Set FFCA, so the runtime knows that we're not "in lisp".
ldr reg_OCFP, =foreign_function_call_active
str pc, [reg_OCFP]
#ifndef LISP_FEATURE_GENCGC
// Copy the current allocation pointer out from the symbol.
ldr reg_OCFP, =dynamic_space_free_pointer
ldr reg_NFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
str reg_NFP, [reg_OCFP]
#endif
// Leave PSEUDO-ATOMIC and check for interrupts.
str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
cmp reg_OCFP, #0
blxne reg_OCFP
// Restore saved registers.
fldmfdd sp!, {d8-d15}
ldmfd sp!, {x3-x11, lr}
bx lr
.size call_into_lisp, .-call_into_lisp
.frame_pointer_address: .word current_control_frame_pointer
.stack_pointer_address: .word current_control_stack_pointer
.align
.global call_into_c
.type call_into_c, %function
call_into_c:
// At this point, we have:
// X8 -- C function to call. This routine doesn't support
// thumb interworking, but linkage-table does, so we
// don't have to.
// LR -- Return address within the code component.
// OCFP -- First C register argument.
// NARGS -- Second C register argument.
// NL2 -- Third C register argument.
// NL3 -- Fourth C register argument.
// All other C arguments are already stashed on the C stack.
// We need to convert our return address to a GC-safe format,
// build a stack frame to count for the "foreign" frame,
// switch to C mode, move the register arguments to the
// correct locations, call the C function, move the result to
// the correct location, switch back to Lisp mode, tear down
// our stack frame, restore the return address, and return to
// our caller.
// We have ONE unboxed scratch register: NFP. Use it as a
// temporary while we convert the (unboxed) return address to
// a (fixnum) offset within the component.
sub reg_NFP, reg_LR, reg_CODE
add reg_NFP, reg_NFP, #OTHER_POINTER_LOWTAG
// Build a Lisp stack frame. We need to stash our frame link,
// the code component, and our return offset. Frame link goes
// in slot 0 (OCFP-SAVE-OFFSET), the offset (a FIXNUM) goes in
// slot 1 (LRA-SAVE-OFFSET), and reg_CODE goes in slot 2. The
// debugger knows about this layout (see COMPUTE-CALLING-FRAME
// in SYS:SRC;CODE;DEBUG-INT.LISP). The stack is aligned, so
// we can use X0 (a boxed register) as our temporary.
ldr reg_X0, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
add reg_X0, reg_X0, #12
str reg_X0, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
str reg_CFP, [reg_X0, #-12]
str reg_NFP, [reg_X0, #-8]
str reg_CODE, [reg_X0, #-4]
// Enter PSEUDO-ATOMIC.
str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
// Save the lisp stack and frame pointers.
ldr reg_NFP, .stack_pointer_address
str reg_X0, [reg_NFP]
sub reg_X0, reg_X0, #12
ldr reg_NFP, .frame_pointer_address
str reg_X0, [reg_NFP]
// We're done with X0, and we need to use OCFP when leaving
// pseudo-atomic, so move the first of the C register
// arguments to its final resting place now.
mov x0, reg_OCFP
// Set FFCA, so the runtime knows that we're not "in lisp".
ldr reg_NFP, =foreign_function_call_active
str pc, [reg_NFP]
#ifndef LISP_FEATURE_GENCGC
// Copy the current allocation pointer out from the symbol.
ldr reg_OCFP, =dynamic_space_free_pointer
ldr reg_NFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
str reg_NFP, [reg_OCFP]
#endif
// Leave PSEUDO-ATOMIC and check for interrupts.
str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
cmp reg_OCFP, #0
blxne reg_OCFP
// Now that we're in C mode, move the remaining register args
// into place.
mov x1, reg_NARGS
mov x2, reg_NL2
mov x3, reg_NL3
// And call the C function. We don't support interworking
// here because we have to be able to pass the function
// pointer in a boxed register, but the linkage-table is quite
// capable of doing a tail-call to a Thumb routine.
//
// X8 is important for undefined_alien_function.
blx reg_X8
// We're back. Our main tasks are to move the C return value
// to where Lisp expects it, and to re-establish the Lisp
// environment.
// Stash the return value into NARGS for Lisp.
mov reg_NARGS, x0
// For returning long-long, and doubles with softfp.
mov reg_NL3, x1
// Re-establish NIL.
ldr reg_NULL, .known_nil
// Blank the boxed registers.
mov reg_X0, #0
mov reg_X1, #0
mov reg_X2, #0
mov reg_LEXENV, #0
mov reg_X8, #0
mov reg_CODE, #0
// Enter PSEUDO-ATOMIC.
str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
// Clear FFCA, so the runtime knows that we're "in lisp".
ldr reg_OCFP, =foreign_function_call_active
str reg_X2, [reg_OCFP]
#ifndef LISP_FEATURE_GENCGC
// Copy the current allocation pointer into the symbol.
ldr reg_OCFP, =dynamic_space_free_pointer
ldr reg_OCFP, [reg_OCFP]
str reg_OCFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
#endif
// Restore the Lisp stack and frame pointers, but store the
// control frame pointer in reg_NFP (saving a register move
// later).
ldr reg_NFP, .stack_pointer_address
ldr reg_CFP, [reg_NFP]
str reg_CFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
ldr reg_NFP, .frame_pointer_address
ldr reg_NFP, [reg_NFP]
// Leave PSEUDO-ATOMIC and check for interrupts.
str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
cmp reg_OCFP, #0
blxne reg_OCFP
// Restore our caller state from our stack frame.
ldr reg_CODE, [reg_NFP, #8]
ldr reg_NL2, [reg_NFP, #4]
ldr reg_CFP, [reg_NFP]
str reg_NFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
// Restore our return address... into the program counter.
sub reg_NL2, reg_NL2, #OTHER_POINTER_LOWTAG
add reg_PC, reg_NL2, reg_CODE
.size call_into_c, .-call_into_c
/* Trampolines, like on SPARC, use Lisp calling conventions. */
.align 3
.global undefined_tramp
.type undefined_tramp, %object
.word SIMPLE_FUN_HEADER_WIDETAG
.equ undefined_tramp, .+1
.word undefined_tramp
.known_nil:
.word NIL
.word NIL
.word NIL
.word NIL
.word NIL
// As in ppc-assem.S, point reg_CODE to the header with a
// function lowtag... Which the address already has.
ldr reg_CODE, =undefined_tramp
// The magic (undefined) "BREAK_POINT" instruction.
.word 0xe7f001f0
// Error arguments for an undefined function.
.byte trap_Error
.byte .error_args_end - . - 1
.byte UNDEFINED_FUN_ERROR
// Need to indicate reg_LEXENV here, which is X3. Encoding
// rules are to produce an "sc-offset" with the SC number in
// the low six bits and the offset (3 in our case) in the
// high $n$ bits. sc_DescriptorReg happens to be 5, but we
// should use the constant for it. So long as the overall
// value of the sc-offset is less than 254, we can use a
// single byte. Overflowing that will take having the SC
// number being 30 or 31, and as of this writing the highest
// SC number is sc_CatchBlock at 16. It would also take an
// offset of 7, not the 3 that we use for LEXENV.
.byte sc_DescriptorReg + (0x40 * 3)
.error_args_end:
.align 3
.global undefined_alien_function
.type undefined_alien_function, %object
.word SIMPLE_FUN_HEADER_WIDETAG
.equ undefined_alien_function, .+1
.word undefined_alien_function
.word NIL
.word NIL
.word NIL
.word NIL
.word NIL
undefined_alien_function:
ldr reg_CODE, = undefined_alien_function
// The magic (undefined) "BREAK_POINT" instruction.
.word 0xe7f001f0
// Error arguments for an undefined function.
.byte trap_Error
.byte 4
.byte UNDEFINED_ALIEN_FUN_ERROR
// Encode unsigned X8, which comes from call_into_c
.byte 0xFE
.byte 0x11
.byte 0x02
.align 3
.global closure_tramp
.type closure_tramp, %object
.word SIMPLE_FUN_HEADER_WIDETAG
.equ closure_tramp, .+1
.word closure_tramp
.word NIL
.word NIL
.word NIL
.word NIL
.word NIL
ldr reg_LEXENV, [reg_LEXENV, #FDEFN_FUN_OFFSET]
ldr reg_CODE, [reg_LEXENV, #CLOSURE_FUN_OFFSET]
add reg_PC, reg_CODE, #SIMPLE_FUN_CODE_OFFSET
.align 3
.global funcallable_instance_tramp
.type funcallable_instance_tramp, %object
.word SIMPLE_FUN_HEADER_WIDETAG
.equ funcallable_instance_tramp, .+1
.word funcallable_instance_tramp
.word NIL
.word NIL
.word NIL
.word NIL
.word NIL
ldr reg_LEXENV, [reg_LEXENV, #FUNCALLABLE_INSTANCE_FUNCTION_OFFSET]
ldr reg_CODE, [reg_LEXENV, #CLOSURE_FUN_OFFSET]
add reg_PC, reg_CODE, #SIMPLE_FUN_CODE_OFFSET
// FIXME-ARM: The following is random garbage, to make
// code/debug-int compile. To get the debugger working, this
// needs to be implemented.
.align
.global fun_end_breakpoint_guts
.type fun_end_breakpoint_guts, %object
fun_end_breakpoint_guts:
.global fun_end_breakpoint_trap
.type fun_end_breakpoint_trap, %function
fun_end_breakpoint_trap:
b fun_end_breakpoint_trap
.global fun_end_breakpoint_end
fun_end_breakpoint_end:
#ifdef LISP_FEATURE_GENCGC
.align
.global alloc_tramp
.type alloc_tramp, %function
alloc_tramp:
stmfd sp!, {x4, x6, x12, lr}
ldr x4, =foreign_function_call_active
str pc, [x4]
ldr x4, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
add x6, x4, #8*4
str x6, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
// Create a new frame and save descriptor regs on the stack
// for the GC to see.
str reg_CFP, [x4, #0]
str reg_NULL, [x4, #4]
str reg_CODE, [x4, #8]
add x4, x4, #3*4
stmea x4, {x0-reg_LEXENV, x8}
ldr x0, [sp, #4*4]
fstmfdd sp!, {d0-d7}
mov lr, pc
ldr pc,=alloc
fldmfdd sp!, {d0-d7}
str x0, [sp, #4*4]
ldr x4, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
ldmea x4, {x0-reg_LEXENV, x8}
sub x4, x4, #8*4
str x4, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
ldr x4, =foreign_function_call_active
mov x6, #0
str x6, [x4]
ldmfd sp!, {x4, x6, x12, lr}
bx lr
.align
.global fpu_save
.type fpu_save, %function
fpu_save:
fstmiad x0, {d0-d7}
bx lr
.align
.global fpu_restore
.type fpu_restore, %function
fpu_restore:
add x0, x0, #16
fldmiad x0, {d0-d7}
bx lr
.align
.global do_pending_interrupt
.type do_pending_interrupt, %function
do_pending_interrupt:
.word 0xe7f001f0
.byte trap_PendingInterrupt
.byte 0
.byte 0
.byte 0
bx lr
#endif

View file

@ -0,0 +1,100 @@
/*
* This is the ARM Linux incarnation of arch-dependent OS-dependent
* routines. See also "linux-os.c".
*/
/*
* This software is part of the SBCL system. See the README file for
* more information.
*
* This software is derived from the CMU CL system, which was
* written at Carnegie Mellon University and released into the
* public domain. The software is in the public domain and is
* provided with absolutely no warranty. See the COPYING and CREDITS
* files for more information.
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/file.h>
#include "sbcl.h"
#include "./signal.h"
#include "os.h"
#include "arch.h"
#include "globals.h"
#include "interrupt.h"
#include "interr.h"
#include "lispregs.h"
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "validate.h"
size_t os_vm_page_size;
#ifdef LISP_FEATURE_SB_THREAD
#error "Define threading support functions"
#else
int arch_os_thread_init(struct thread *thread) {
stack_t sigstack;
/* Signal handlers are normally run on the main stack, but we've
* swapped stacks, require that the control stack contain only
* boxed data, and expands upwards while the C stack expands
* downwards. */
sigstack.ss_sp=((void *) thread)+dynamic_values_bytes;
sigstack.ss_flags=0;
sigstack.ss_size = 32*SIGSTKSZ;
if(sigaltstack(&sigstack,0)<0)
lose("Cannot sigaltstack: %s\n",strerror(errno));
return 1; /* success */
}
int arch_os_thread_cleanup(struct thread *thread) {
return 1; /* success */
}
#endif
os_context_regis
ter_t *
os_context_register_addr(os_context_t *context, int offset)
{
return &(context->uc_mcontext.regs[offset]);
}
os_context_register_t *
os_context_pc_addr(os_context_t *context)
{
return &(context->uc_mcontext.pc);
}
os_context_register_t *
os_context_lr_addr(os_context_t *context)
{
return os_context_register_addr(context, reg_LR);
}
sigset_t *
os_context_sigmask_addr(os_context_t *context)
{
return &(context->uc_sigmask);
}
void
os_restore_fp_control(os_context_t *context)
{
/* FIXME: Implement. */
}
void
os_flush_icache(os_vm_address_t address, os_vm_size_t length)
{
os_vm_address_t end_address
= (os_vm_address_t)(((pointer_sized_uint_t) address) + length);
__clear_cache(address, end_address);
}

View file

@ -0,0 +1,16 @@
#ifndef _ARM_LINUX_OS_H
#define _ARM_LINUX_OS_H
typedef struct ucontext os_context_t;
typedef long os_context_register_t;
static inline os_context_t *arch_os_get_context(void **void_context)
{
return (os_context_t *) *void_context;
}
unsigned long os_context_fp_control(os_context_t *context);
#define RESTORE_FP_CONTROL_FROM_CONTEXT
void os_restore_fp_control(os_context_t *context);
#endif /* _ARM_LINUX_OS_H */

View file

@ -0,0 +1,46 @@
/*
* This software is part of the SBCL system. See the README file for
* more information.
*
* This software is derived from the CMU CL system, which was
* written at Carnegie Mellon University and released into the
* public domain. The software is in the public domain and is
* provided with absolutely no warranty. See the COPYING and CREDITS
* files for more information.
*/
#define NREGS (31)
#ifdef LANGUAGE_ASSEMBLY
# define REG(num) w##num
#else
# define REG(num) (num)
#endif
#define reg_R0 REG(0)
#define reg_R1 REG(1)
#define reg_R2 REG(2)
#define reg_LEXENV REG(3)
#define reg_NL2 REG(4)
#define reg_CODE REG(5)
#define reg_NL3 REG(6)
#define reg_OCFP REG(7)
#define reg_R8 REG(8)
#define reg_NFP REG(9)
#define reg_NULL REG(10)
#define reg_CFP REG(11)
#define reg_NARGS REG(12)
#define reg_NSP REG(13)
#define reg_LR REG(30)
#define reg_PC REG(15)
#define REGNAMES \
"R0", "R1", "R2", "LEXENV", \
"NL2", "CODE", "NL3", "OCFP", \
"R8", "NFP", "NULL", "CFP", \
"NARGS", "NSP", "LR", "PC"
#define BOXED_REGISTERS { \
reg_R0, reg_R1, reg_R2, reg_LEXENV, reg_R8, reg_CODE \
}

View file

@ -130,7 +130,7 @@ call_info_from_context(struct call_info *info, os_context_t *context)
uword_t pc;
info->interrupted = 1;
#if !defined(LISP_FEATURE_ARM)
#if !defined(LISP_FEATURE_ARM) && !defined(LISP_FEATURE_ARM64)
if (lowtag_of(*os_context_register_addr(context, reg_CODE))
== FUN_POINTER_LOWTAG) {
/* We tried to call a function, but crapped out before $CODE could

View file

@ -823,7 +823,7 @@ size_tiny_boxed(lispobj *where)
/* Note: on the sparc we don't have to do anything special for fdefns, */
/* 'cause the raw-addr has a function lowtag. */
#if (!defined(LISP_FEATURE_SPARC)) && (!defined(LISP_FEATURE_ARM))
#if !defined(LISP_FEATURE_SPARC) && !defined(LISP_FEATURE_ARM) && !defined(LISP_FEATURE_ARM64)
static sword_t
scav_fdefn(lispobj *where, lispobj object)
{
@ -2179,7 +2179,7 @@ gc_init_tables(void)
scavtab[UNBOUND_MARKER_WIDETAG] = scav_immediate;
scavtab[NO_TLS_VALUE_MARKER_WIDETAG] = scav_immediate;
scavtab[INSTANCE_HEADER_WIDETAG] = scav_instance;
#if defined(LISP_FEATURE_SPARC) || defined(LISP_FEATURE_ARM)
#if defined(LISP_FEATURE_SPARC) || defined(LISP_FEATURE_ARM) || defined(LISP_FEATURE_ARM64)
scavtab[FDEFN_WIDETAG] = scav_boxed;
#else
scavtab[FDEFN_WIDETAG] = scav_fdefn;

View file

@ -73,7 +73,7 @@ NWORDS(uword_t x, uword_t n_bits)
/* FIXME: Shouldn't this be defined in sbcl.h? */
#if defined(LISP_FEATURE_SPARC) || defined(LISP_FEATURE_ARM)
#if defined(LISP_FEATURE_SPARC) || defined(LISP_FEATURE_ARM) || defined(LISP_FEATURE_ARM64)
#define FUN_RAW_ADDR_OFFSET 0
#else
#define FUN_RAW_ADDR_OFFSET (offsetof(struct simple_fun, code) - FUN_POINTER_LOWTAG)

View file

@ -507,6 +507,9 @@ write_generation_stats(FILE *file)
#elif defined(LISP_FEATURE_ARM)
#define FPU_STATE_SIZE 8
long long fpu_state[FPU_STATE_SIZE];
#elif defined(LISP_FEATURE_ARM64)
#define FPU_STATE_SIZE 32
long fpu_state[FPU_STATE_SIZE];
#endif
/* This code uses the FP instructions which may be set up for Lisp

View file

@ -38,7 +38,7 @@ lispobj *current_binding_stack_pointer;
/* ALLOCATION_POINTER is x86 or RT. Anyone want to do an RT port? */
# if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64) && !defined(LISP_FEATURE_ARM)
# if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64) && !defined(LISP_FEATURE_ARM) && !defined(LISP_FEATURE_ARM64)
/* The Object Formerly Known As current_dynamic_space_free_pointer */
lispobj *dynamic_space_free_pointer;
#endif

View file

@ -113,7 +113,7 @@ extern void globals_init(void);
# define EXTERN(name,bytes) .global name
# endif
/**/
# ifdef LISP_FEATURE_ARM
# if defined(LISP_FEATURE_ARM) || defined(LISP_FEATURE_ARM64)
# define EXTERN(name,bytes) .global name
# endif

View file

@ -641,7 +641,7 @@ build_fake_control_stack_frames(struct thread *th,os_context_t *context)
/* Build a fake stack frame or frames */
#if !defined(LISP_FEATURE_ARM)
#if !defined(LISP_FEATURE_ARM) && !defined(LISP_FEATURE_ARM64)
access_control_frame_pointer(th) =
(lispobj *)(uword_t)
(*os_context_register_addr(context, reg_CSP));
@ -732,7 +732,7 @@ fake_foreign_function_call(os_context_t *context)
*os_context_register_addr(context, reg_BSP));
#endif
#ifdef LISP_FEATURE_ARM
#if defined(LISP_FEATURE_ARM) || defined LISP_FEATURE_ARM64
/* Stash our control stack pointer */
bind_variable(INTERRUPTED_CONTROL_STACK_POINTER,
SymbolValue(CONTROL_STACK_POINTER, thread),
@ -778,7 +778,7 @@ undo_fake_foreign_function_call(os_context_t *context)
/* Undo dynamic binding of FREE_INTERRUPT_CONTEXT_INDEX */
unbind(thread);
#ifdef LISP_FEATURE_ARM
#if defined(LISP_FEATURE_ARM) || defined LISP_FEATURE_ARM64
/* Restore our saved control stack pointer */
SetSymbolValue(CONTROL_STACK_POINTER,
SymbolValue(INTERRUPTED_CONTROL_STACK_POINTER,
@ -1581,7 +1581,7 @@ arrange_return_to_c_function(os_context_t *context,
*os_context_npc_addr(context) =
4 + *os_context_pc_addr(context);
#endif
#if defined(LISP_FEATURE_SPARC) || defined(LISP_FEATURE_ARM)
#if defined(LISP_FEATURE_SPARC) || defined(LISP_FEATURE_ARM) || defined(LISP_FEATURE_ARM64)
*os_context_register_addr(context,reg_CODE) =
(os_context_register_t)(fun + FUN_POINTER_LOWTAG);
#endif
@ -1600,7 +1600,7 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
}
// These have undefined_alien_function tramp in x-assem.S
#if !(defined(LISP_FEATURE_X86_64) || defined(LISP_FEATURE_ARM))
#if !(defined(LISP_FEATURE_X86_64) || defined(LISP_FEATURE_ARM) || defined(LISP_FEATURE_ARM64))
/* KLUDGE: Theoretically the approach we use for undefined alien
* variables should work for functions as well, but on PPC/Darwin
* we get bus error at bogus addresses instead, hence this workaround,

View file

@ -93,7 +93,7 @@ clear_pseudo_atomic_interrupted(struct thread *thread)
#undef LISPOBJ_ASM_SUFFIX
#elif defined(LISP_FEATURE_ARM)
#elif defined(LISP_FEATURE_ARM) || defined LISP_FEATURE_ARM64
static inline int
get_pseudo_atomic_atomic(struct thread *thread)
{

View file

@ -63,7 +63,7 @@ ldso_stub__~A: ; \\
#endif
#include \"sbcl.h\""
#!+arm "
#!+ (or arm arm64) "
#define LDSO_STUBIFY(fct) \\
.align ; \\
.global ldso_stub__ ## fct ; \\