1 #define LANGUAGE_ASSEMBLY
7 #include "genesis/closure.h"
8 #include "genesis/funcallable-instance.h"
9 #include "genesis/fdefn.h"
10 #include "genesis/static-symbols.h"
11 #include "genesis/simple-fun.h"
12 #include "genesis/symbol.h"
14 #define STATIC_SYMBOL_VALUE(sym) [reg_NULL, #(((sym)-NIL)+SYMBOL_VALUE_OFFSET)]
17 .global call_into_lisp
18 .type call_into_lisp, %function
20 @@ At this point, we have:
22 @@ R1 - pointer to args
23 @@ R2 - number of args (unboxed)
24 @@ There will be no more than three args, so we don't need to
25 @@ worry about parameters to be passed on the stack.
27 @@ All registers other than R0-R3 and R12 are callee-saves.
28 stmfd sp!, {r4-r11, lr}
30 @@ Start by finding NIL.
31 ldr reg_NULL, .known_nil
34 mov reg_NARGS, r2, lsl #2
36 @@ Move args pointer out of the way of the args to be loaded.
39 @@ Move the function to its passing location.
42 @@ Clear the boxed registers that don't already have something
47 @@ Find the lisp stack and frame pointers. We're allocating a
48 @@ new lisp stack frame, so load the stack pointer into CFP.
49 @@ And we need the frame pointer, but OCFP is in use, so use
51 ldr reg_NFP, .frame_pointer_address
52 ldr reg_CFP, .stack_pointer_address
53 ldr reg_NFP, [reg_NFP]
54 ldr reg_CFP, [reg_CFP]
56 @@ Enter PSEUDO-ATOMIC.
57 str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
59 @@ Copy the current allocation pointer into the symbol.
60 ldr reg_OCFP, =dynamic_space_free_pointer
61 ldr reg_OCFP, [reg_OCFP]
62 str reg_OCFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
64 @@ Clear FFCA, so the runtime knows that we're "in lisp".
65 ldr reg_OCFP, =foreign_function_call_active
66 str reg_R2, [reg_OCFP]
68 @@ We need to set up the lisp stack pointer and the basics of
69 @@ our stack frame while we're still in P-A. Any sooner and
70 @@ our stack frame can be clobbered by a stray interrupt, any
71 @@ later and we can end up with a half-configured stack frame
72 @@ when we catch a stray interrupt.
74 @@ Allocate our frame and set up the Lisp stack pointer
75 add reg_OCFP, reg_CFP, #8
76 str reg_OCFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
78 @@ Set up the "frame link"
79 str reg_NFP, [reg_OCFP, #-8]
81 @@ Set up the return address
83 str reg_NL3, [reg_OCFP, #-4]
85 @@ Leave PSEUDO-ATOMIC and check for interrupts.
86 str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
87 ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
88 cmp reg_OCFP, reg_NULL
89 movlt reg_OCFP, reg_OCFP, lsl #N_FIXNUM_TAG_BITS
92 @@ Load our function args. Cleverness abounds!
93 rsb reg_NL3, reg_NARGS, #8
95 ldr reg_R2, [reg_R8, #8]
96 ldr reg_R1, [reg_R8, #4]
99 @@ Load the closure-fun (or simple-fun-self), in case we're
100 @@ trying to call a closure.
101 ldr reg_CODE, [reg_LEXENV, #CLOSURE_FUN_OFFSET]
103 @@ And, finally, call into Lisp!
104 add reg_PC, reg_CODE, #SIMPLE_FUN_CODE_OFFSET
107 .equ .lra, .+OTHER_POINTER_LOWTAG
108 .word RETURN_PC_HEADER_WIDETAG
110 @@ Correct stack pointer for return processing.
111 streq reg_OCFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
113 @@ Enter PSEUDO-ATOMIC.
114 str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
116 @@ Save the lisp stack and frame pointers.
117 ldr reg_NFP, .frame_pointer_address
118 str reg_CFP, [reg_NFP]
119 ldr reg_NFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
120 ldr reg_OCFP, .stack_pointer_address
121 str reg_NFP, [reg_OCFP]
123 @@ Set FFCA, so the runtime knows that we're not "in lisp".
124 ldr reg_OCFP, =foreign_function_call_active
127 @@ Copy the current allocation pointer out from the symbol.
128 ldr reg_OCFP, =dynamic_space_free_pointer
129 ldr reg_NFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
130 str reg_NFP, [reg_OCFP]
132 @@ Leave PSEUDO-ATOMIC and check for interrupts.
133 str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
134 ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
135 cmp reg_OCFP, reg_NULL
136 movlt reg_OCFP, reg_OCFP, lsl #N_FIXNUM_TAG_BITS
139 @@ Restore saved registers.
140 ldmfd sp!, {r4-r11, lr}
142 .size call_into_lisp, .-call_into_lisp
143 .frame_pointer_address: .word current_control_frame_pointer
144 .stack_pointer_address: .word current_control_stack_pointer
149 .type call_into_c, %function
151 @@ At this point, we have:
152 @@ R8 -- C function to call. This routine doesn't support
153 @@ thumb interworking, but linkage-table does, so we
155 @@ LR -- Return address within the code component.
156 @@ OCFP -- First C register argument.
157 @@ NARGS -- Second C register argument.
158 @@ NL2 -- Third C register argument.
159 @@ NL3 -- Fourth C register argument.
160 @@ All other C arguments are already stashed on the C stack.
162 @@ We need to convert our return address to a GC-safe format,
163 @@ build a stack frame to count for the "foreign" frame,
164 @@ switch to C mode, move the register arguments to the
165 @@ correct locations, call the C function, move the result to
166 @@ the correct location, switch back to Lisp mode, tear down
167 @@ our stack frame, restore the return address, and return to
170 @@ We have ONE unboxed scratch register: NFP. Use it as a
171 @@ temporary while we convert the (unboxed) return address to
172 @@ a (fixnum) offset within the component.
173 sub reg_NFP, reg_LR, reg_CODE
174 add reg_NFP, reg_NFP, #OTHER_POINTER_LOWTAG
176 @@ Build a Lisp stack frame. We need to stash our frame link,
177 @@ the code component, and our return offset. Frame link goes
178 @@ in slot 0 (OCFP-SAVE-OFFSET), the offset (a FIXNUM) goes in
179 @@ slot 1 (LRA-SAVE-OFFSET), and reg_CODE goes in slot 2. The
180 @@ debugger knows about this layout (see COMPUTE-CALLING-FRAME
181 @@ in SYS:SRC;CODE;DEBUG-INT.LISP). The stack is aligned, so
182 @@ we can use R0 (a boxed register) as our temporary.
183 ldr reg_R0, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
184 add reg_R0, reg_R0, #12
185 str reg_R0, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
186 str reg_CFP, [reg_R0, #-12]
187 str reg_NFP, [reg_R0, #-8]
188 str reg_CODE, [reg_R0, #-4]
190 @@ Enter PSEUDO-ATOMIC.
191 str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
193 @@ Save the lisp stack and frame pointers.
194 ldr reg_NFP, .stack_pointer_address
195 str reg_R0, [reg_NFP]
196 sub reg_R0, reg_R0, #12
197 ldr reg_NFP, .frame_pointer_address
198 str reg_R0, [reg_NFP]
200 @@ We're done with R0, and we need to use OCFP when leaving
201 @@ pseudo-atomic, so move the first of the C register
202 @@ arguments to its final resting place now.
205 @@ Set FFCA, so the runtime knows that we're not "in lisp".
206 ldr reg_NFP, =foreign_function_call_active
209 @@ Copy the current allocation pointer out from the symbol.
210 ldr reg_OCFP, =dynamic_space_free_pointer
211 ldr reg_NFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
212 str reg_NFP, [reg_OCFP]
214 @@ Leave PSEUDO-ATOMIC and check for interrupts.
215 str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
216 ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
217 cmp reg_OCFP, reg_NULL
218 movlt reg_OCFP, reg_OCFP, lsl #N_FIXNUM_TAG_BITS
221 @@ Now that we're in C mode, move the remaining register args
227 @@ And call the C function. We don't support interworking
228 @@ here because we have to be able to pass the function
229 @@ pointer in a boxed register, but the linkage-table is quite
230 @@ capable of doing a tail-call to a Thumb routine.
233 @@ We're back. Our main tasks are to move the C return value
234 @@ to where Lisp expects it, and to re-establish the Lisp
237 @@ Stash the return value into NARGS for Lisp.
239 @@ For returning long-long, and doubles with softfp.
243 ldr reg_NULL, .known_nil
245 @@ Blank the boxed registers.
253 @@ Enter PSEUDO-ATOMIC.
254 str pc, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
256 @@ Clear FFCA, so the runtime knows that we're "in lisp".
257 ldr reg_OCFP, =foreign_function_call_active
258 str reg_R2, [reg_OCFP]
260 @@ Copy the current allocation pointer into the symbol.
261 ldr reg_OCFP, =dynamic_space_free_pointer
262 ldr reg_OCFP, [reg_OCFP]
263 str reg_OCFP, STATIC_SYMBOL_VALUE(ALLOCATION_POINTER)
265 @@ Restore the Lisp stack and frame pointers, but store the
266 @@ control frame pointer in reg_NFP (saving a register move
268 ldr reg_NFP, .stack_pointer_address
269 ldr reg_CFP, [reg_NFP]
270 str reg_CFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
271 ldr reg_NFP, .frame_pointer_address
272 ldr reg_NFP, [reg_NFP]
274 @@ Leave PSEUDO-ATOMIC and check for interrupts.
275 str reg_NULL, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_ATOMIC)
276 ldr reg_OCFP, STATIC_SYMBOL_VALUE(PSEUDO_ATOMIC_INTERRUPTED)
277 cmp reg_OCFP, reg_NULL
278 movlt reg_OCFP, reg_OCFP, lsl #N_FIXNUM_TAG_BITS
281 @@ Restore our caller state from our stack frame.
282 ldr reg_CODE, [reg_NFP, #8]
283 ldr reg_NL2, [reg_NFP, #4]
284 ldr reg_CFP, [reg_NFP]
285 str reg_NFP, STATIC_SYMBOL_VALUE(CONTROL_STACK_POINTER)
287 @@ Restore our return address... into the program counter.
288 sub reg_NL2, reg_NL2, #OTHER_POINTER_LOWTAG
289 add reg_PC, reg_NL2, reg_CODE
291 .size call_into_c, .-call_into_c
294 /* Trampolines, like on SPARC, use Lisp calling conventions. */
296 .global undefined_tramp
297 .type undefined_tramp, %object
298 .word SIMPLE_FUN_HEADER_WIDETAG
299 .equ undefined_tramp, .+1
300 .word undefined_tramp
308 @@ The magic (undefined) "BREAK_POINT" instruction.
311 @@ Error arguments for an undefined function.
313 .byte .error_args_end - . - 1
314 .byte UNDEFINED_FUN_ERROR
315 @@ Need to indicate reg_LEXENV here, which is R3. Encoding
316 @@ rules are to produce an "sc-offset" with the SC number in
317 @@ the low six bits and the offset (3 in our case) in the
318 @@ high $n$ bits. sc_DescriptorReg happens to be 5, but we
319 @@ should use the constant for it. So long as the overall
320 @@ value of the sc-offset is less than 254, we can use a
321 @@ single byte. Overflowing that will take having the SC
322 @@ number being 30 or 31, and as of this writing the highest
323 @@ SC number is sc_CatchBlock at 16. It would also take an
324 @@ offset of 7, not the 3 that we use for LEXENV.
325 .byte sc_DescriptorReg + (0x40 * 3)
329 .global closure_tramp
330 .type closure_tramp, %object
331 .word SIMPLE_FUN_HEADER_WIDETAG
332 .equ closure_tramp, .+1
340 ldr reg_LEXENV, [reg_LEXENV, #FDEFN_FUN_OFFSET]
341 ldr reg_CODE, [reg_LEXENV, #CLOSURE_FUN_OFFSET]
342 add reg_PC, reg_CODE, #SIMPLE_FUN_CODE_OFFSET
345 .global funcallable_instance_tramp
346 .type funcallable_instance_tramp, %object
347 .word SIMPLE_FUN_HEADER_WIDETAG
348 .equ funcallable_instance_tramp, .+1
349 .word funcallable_instance_tramp
356 ldr reg_LEXENV, [reg_LEXENV, #FUNCALLABLE_INSTANCE_FUNCTION_OFFSET]
357 ldr reg_CODE, [reg_LEXENV, #CLOSURE_FUN_OFFSET]
358 add reg_PC, reg_CODE, #SIMPLE_FUN_CODE_OFFSET
360 @@ FIXME-ARM: The following is random garbage, to make
361 @@ code/debug-int compile. To get the debugger working, this
362 @@ needs to be implemented.
364 .global fun_end_breakpoint_guts
365 .type fun_end_breakpoint_guts, %object
366 fun_end_breakpoint_guts:
367 .global fun_end_breakpoint_trap
368 .type fun_end_breakpoint_trap, %function
369 fun_end_breakpoint_trap:
370 b fun_end_breakpoint_trap
371 .global fun_end_breakpoint_end
372 fun_end_breakpoint_end: