FunctionPackage: systemToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version

memref

Arguments: object offset pos type &optional coerce

This function and the related function memref-int calculate an address in memory and performs a read from that address. This function takes an object as an argument while memref-int takes an address.

The width and style of the read depends upon the type. The possible values for the type argument are shown in a table below.

Except when the type argument is :lisp, the result is always a number, either an integer or float. The type and possible range of that number depends on the type argument. This function is setf'able. The setf function writes to the addressed memory instead of reading from it.

The object argument may be any Lisp value. If the Lisp value represents an object in the heap, the address of the object (including its tag) is used as the base address in the memory reference. If the Lisp value is an immediate value (a fixnum, a character, ...), then the actual bit representation of the Lisp value (including its tag) is used as the base address in the memory reference.

The offset and pos arguments must be fixnum values. The sum of these two arguments is added to the base address value and the result is treated as an address in memory from which data is fetched, or to which data is stored.

When the coerce argument is non-nil some checking is done in the setf form. The value to be stored is coerced to the required type.

This function provides low-level memory access in Allegro CL. However, even though the name is exported and documented, we recommend against using this function in user code. Instead, we recommend using higher-level functions, such as those linked to below. However, the function is useful in the development cycle for debugging purposes.

WARNING about setf: except when the type is :lisp, the setf function does not do any setf-protection. If it is used to store non-Lisp values into a memory location that was intended to hold a Lisp value, then a non-recoverable error is very likely to occur during the next GC.

The access-types (possible values of type) that are available are as follows:

:unsigned-byte, :signed-byte (1-byte access, 1-byte integer result)  
:unsigned-word, :signed-word (2-byte access, 2-byte integer result)  
:unsigned-long, :signed-long 
  ;; 4-byte access, 4-byte integer result in 32-bit lisps
  ;; undetermined (4-byte or 8-byte) in 64-bit lisps
:unsigned-long32 (4-byte access, 4-byte integer result)
:unsigned-long64 (8-byte access, 8-byte integer result on 64-bit
                  Lisps but 4-byte access, 4-byte integer result
                  on all others)
:fixnum (in 32-bit lisp, 4-byte access, 
         fixnum result [top bits lost on overflow]
         in 64-bit lisp, 8-byte access, 
         fixnum result [top bits lost on overflow])  
:lisp (in 32-bit lisp, 4-byte access, lisp result 
       in 64-bit lisp, 8-byte access, lisp result
       [careful, could confuse gc])  
:single-float (or single-float) (4-byte access, 4-byte single-float result)  
:double-float (or double-float) (8-byte access, 8-byte double-float result) 
:signed-natural 
:unsigned-natural
  ;; For these last two, in 32-bit lisps, these will 
  ;; construct 32-bit values, and in 64-bit lisps, it will construct 
  ;; 64-bit values
:nybble (4-bit -- half-byte -- value)

Be aware that though the access types might look similar to C types, they are not actually identical.

For more information on :signed-natural and :unsigned-natural, see 64 bit Allegro CL Implementations in implementation.htm.

Programming Notes

  1. The function memref is very similar to the memref-int function, but because the base address determination is significantly different the descriptions are different in several subtle but important ways.
  2. In place of using memref and memref-int, operators like single-float-to-shorts, double-float-to-shorts, shorts-to-single-float, shorts-to-double-float may be used for accessing/creating floats. A foreign type can be defined (see ftype.htm) to access arbitrary memory areas. See also write-vector.
  3. When optimization is declared at a sufficient level, the call to memref and to the setf form, is compiled in line and is thus very efficient. Note that when the optional coerce argument is supplied, then the call is not open-coded when compiled.
  4. When the coerce argument is non-nil some checking is done in the setf form. The value to be stored is coerced to the required type. This is especially useful when the argument is a float value, but of the wrong type, as in
    (setf (sys:memref-int array-address 0 0 :double-float :coerce) 1.0s0)
    
  5. The offset and pos are equivalent and interchangeable. They are both added as byte-offsets to the base address to determine the final memory address. Usually, the offset is some constant that adjusts for the start of the data in some object being accessed.

    An offset is necessary to access Lisp objects because a Lisp object pointer contains tag bits in the low-order positions. When the pointer is treated as a machine address, the numeric value is not the address of the Lisp data object in memory. The address is normally lower than the address of the object, by a fixed amount determined by the tag bits in the pointer.

    Some of the offsets for Lisp objects are described in [Allegro directory]/misc/lisp.h in the Allegro CL distribution. In an earlier version of this documentation, it was stated that the offsets are available in Lisp itself by calling certain otherwise undocumented functions. That statement was incorrect. The offsets are not reliably available with Lisp.

  6. In all 32-bit implementations, a Lisp fixnum is represented by the numeric value shifted left by 2 bits. Thus, the fixnum 1 is represented by the machine word #x00000004. When a fixnum is passed as the object argument to memref, the effective memory address is the (32-bit) word in memory indexed by the fixnum value. Thus, the fixnum 0 addresses the 0-th word at address 0, the fixnum 1 addresses the 1-th word at address 4, etc. This feature is very useful when referencing blocks of foreign storage aligned on a word boundary. The numeric address of the block is divided by 4 to obtain the fixnum word index of the block. The index is always a fixnum since all memory addresses must fit within a 32-bit machine word. The foreign type facility recognizes this interpretation of fixnums as the :aligned foreign pointer type.
  7. In the 64-bit implementations, a Lisp fixnum is a numeric value shifted left by 3 bits. Thus a fixnum represents a double-word index and the above discussion must be modified accordingly.
  8. One application of memref-int is to access large blocks of memory (larger than array-total-size-limit).

    For example, if the variable addr holds the foreign address of a large block of memory with a length stored in the size variable, the following loop could be used to extract bytes from the block:

    ((let ((addr/4 (ash addr -2)))
      (dotimes (i (size) (memref addr/4 i 0 :byte)))
    

    If the index i can rise above the fixnum limit, the address arithmetic must be performed before the call to memref:

    (let ((addr/4 (ash addr -2))
          (size/4 (ash size -2)))
      (dotimes (i size/4)
        (dotimes (j 4)
          (memref (+ addr/4 i) j 0 :byte))))
    

    The second inner loop is needed since only the offset and pos arguments allow byte addresses to be reached.

    If the addresses in the memory block cross over into the negative fixnum range we need to break up the ranges of iteration:

    (let ((addr/4 (ash addr -2))
          (size/4 (ash size -2)))
      (if* (and (< addr/4 most-positive-fixnum)
    	    (<= size/4 (- most-positive-fixnum addr/4)))
    	then 
    	;; this is the simple case, all addresses in the positive
    	;; fixnum range
    	(dotimes (i size/4)
                   (dotimes (j 4)
                     (memref (+ addr/4 i) j 0 :byte))))
    	elseif (< addr/4 most-positive-fixnum)
    	then 
    	;; the starting address is in the positive range
    	;; but the increment will cross over eventually
    	;; do the positive range first
    	(dotimes (i (- most-positive-fixnum addr/4))
                   (dotimes (j 4)
                     (memref (+ addr/4 i) j 0 :byte)))
    	;; then the negative range
            (dotimes (i (- size/4 (- most-positive-fixnum addr/4)))
                   (dotimes (j 4)
                     (memref (+ most-negative-fixnum i) j 0 :byte)))
            else  
    	;; starting address is in the negative fixnum range
    	;; we create a negative fixnum that represents the word index
    	(setq addr/4 (+ most-negative-fixnum (- addr/4 most-positive-fixnum)))
    	(dotimes (i size/4)
               (dotimes (j 4)
                 (memref (+ addr/4 i) j 0 :byte)))
    

Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page was not revised from the 10.0 page.
Created 2019.8.20.

ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version