Make-array in SBCL

431 Views Asked by At

How does make-array work in SBCL? Are there some equivalents of new and delete operators in C++, or is it something else, perhaps assembler level?

I peeked into the source, but didn't understand anything.

1

There are 1 best solutions below

2
coredump On BEST ANSWER

When using SBCL compiled from source and an environment like Emacs/Slime, it is possible to navigate the code quite easily using M-. (meta-point). Basically, the make-array symbol is bound to multiple things: deftransform definitions, and a defun. The deftransform are used mostly for optimization, so better just follow the function, first.

The make-array function delegates to an internal make-array% one, which is quite complex: it checks the parameters, and dispatches to different specialized implementation of arrays, based on those parameters: a bit-vector is implemented differently than a string, for example.

If you follow the case for simple-array, you find a function which calls allocate-vector-with-widetag, which in turn calls allocate-vector.

Now, allocate-vector is bound to several objects, multiple defoptimizers forms, a function and a define-vop form.

The function is only:

(defun allocate-vector (type length words)
  (allocate-vector type length words))

Even if it looks like a recursive call, it isn't.

The define-vop form is a way to define how to compile a call to allocate-vector. In the function, and anywhere where there is a call to allocate-vector, the compiler knows how to write the assembly that implements the built-in operation. But the function itself is defined so that there is an entry point with the same name, and a function object that wraps over that code.

define-vop relies on a Domain Specific Language in SBCL that abstracts over assembly. If you follow the definition, you can find different vops (virtual operations) for allocate-vector, like allocate-vector-on-heap and allocate-vector-on-stack.

Allocation on heap translates into a call to calc-size-in-bytes, a call to allocation and put-header, which most likely allocates memory and tag it (I followed the definition to src/compiler/x86-64/alloc.lisp). How memory is allocated (and garbage collected) is another problem.

allocation emits assembly code using %alloc-tramp, which in turns executes the following:

(invoke-asm-routine 'call (if to-r11 'alloc-tramp-r11 'alloc-tramp) node)

There are apparently assembly routines called alloc-tramp-r11 and alloc-tramp, which are predefined assembly instructions. A comment says:

;;; Most allocation is done by inline code with sometimes help
;;; from the C alloc() function by way of the alloc-tramp
;;; assembly routine.

There is a base of C code for the runtime, see for example /src/runtime/alloc.c.

The -tramp suffix stands for trampoline.

Have also a look at src/runtime/x86-assem.S.