Пример #1
0
Файл: bio.c Проект: phoboz/vmx
struct buf* buf_new (
    struct vnode * vp,
    lblkno_t       blkno
    ) {
    struct buf *bp;
    LIST *pBufHead = &vp->v_mount->mnt_buflist;

    for (bp = (struct buf *) LIST_TAIL (pBufHead);
         bp != NULL;
         bp = (struct buf *) LIST_PREV (&bp->b_node)) {
        if ((bp->b_flags & B_BUSY) == 0) {
            bp->b_flags = B_BUSY;
            bp->b_lblkno = 0;
            bp->b_count = 0;
            bp->b_dev   = -1;
            bp->b_vp    = NULL;
            bp->b_error = OK;
            bp->b_resid = 0;

            return (bp);
        }
    }

    return (NULL);
}
Пример #2
0
Val   _lib7_P_Process_exece   (Task* task,  Val arg)   {
    //=====================
    //
    // _lib7_P_Process_exece : String * String list * String list -> 'a
    //
    // Overlay a new process image, using specified environment.
    //
    // This fn gets bound as   exece   in:
    //
    //     src/lib/std/src/posix-1003.1b/posix-process.pkg

    Val  path   =  GET_TUPLE_SLOT_AS_VAL( arg, 0 );
    Val  arglst =  GET_TUPLE_SLOT_AS_VAL( arg, 1 );
    Val	 envlst =  GET_TUPLE_SLOT_AS_VAL( arg, 2 );

    // Use the heap for temp space for
    // the argv[] and envp[] vectors:
    //
    char** cp =  (char**)(task->heap_allocation_pointer);

    #ifdef SIZES_C_64_MYTHRYL_32
	//
	// 8-byte align it:
	//
	cp = (char**) ROUND_UP_TO_POWER_OF_TWO((Unt2)cp, POINTER_BYTESIZE);
    #endif

    char** argv = cp;
    //
    for (Val p = arglst;  p != LIST_NIL;  p = LIST_TAIL(p)) {
        *cp++ = HEAP_STRING_AS_C_STRING(LIST_HEAD(p));
    }
    *cp++ = 0;						// Terminate the argv[].

    char** envp = cp;
    //
    for (Val p = envlst;  p != LIST_NIL;  p = LIST_TAIL(p)) {
        *cp++ = HEAP_STRING_AS_C_STRING(LIST_HEAD(p));
    }
    *cp++ = 0;						// Terminate the envp[].

    int status =  execve( HEAP_STRING_AS_C_STRING(path), argv, envp );

    CHECK_RETURN (task, status)
}
Пример #3
0
Файл: world.c Проект: phoboz/yz
static void init_world_terrain(
  WORLD *world,
  int x,
  int y,
  int cw,
  int ch
  )
{
  int i, sx, sy;
  int mw, mh;
  FIELD *f, *fs, *fm, *fe, *field;

  sx = x / (world->tile_width * cw);
  sy = y / (world->tile_height * ch);

  mw = world->map->w / cw;
  mh = world->map->h / ch;

  for (i = 0; i < mh; i++) {

    f = process_world_row(world, i, mw, sx, sy, cw, ch, (i + 1) % 2);
    if (f != NULL)
      fm = f;

  }

  fs = (FIELD *) LIST_HEAD(&world->fieldList);
  fe = (FIELD *) LIST_TAIL(&world->fieldList);

  for(field = (FIELD *) LIST_TAIL(&world->fieldList);
      field != NULL;
      field = (FIELD *) LIST_PREV(&field->listNode)) {

    f = (FIELD *) LIST_PREV(&field->listNode);
    if (f == NULL)
      break;

    add_world_road_to(world, field, f);
    add_world_road_to(world, field, fs);
    add_world_road_to(world, field, fm);
    add_world_road_to(world, field, fe);
  }

  reveal_world_terrain(world, x, y);
}
Пример #4
0
Val    _lib7_P_Process_exec   (Task* task,  Val arg)   {
    //=====================
    //
    // Mythryl type:  (String, List(String) -> X
    //
    // Overlay a new process image
    //
    // This fn gets bound as   exec   in:
    //
    //     src/lib/std/src/posix-1003.1b/posix-process.pkg

									    ENTER_MYTHRYL_CALLABLE_C_FN("_lib7_P_Process_exec");

    Val path   = GET_TUPLE_SLOT_AS_VAL(arg, 0);
    Val arglst = GET_TUPLE_SLOT_AS_VAL(arg, 1);

    // Use the heap for temp space for the argv[] vector
    //
    char** cp =  (char**) (task->heap_allocation_pointer);

    #ifdef SIZES_C_64_MYTHRYL_32
	//
	// 8-byte align it:
	//
	cp = (char **)ROUNDUP((Unt2)cp, POINTER_BYTESIZE);
    #endif

    char** argv =  cp;
    //
    for (Val p = arglst;  p != LIST_NIL;  p = LIST_TAIL(p)) {
	//
        *cp++ = HEAP_STRING_AS_C_STRING(LIST_HEAD(p));
    }
    *cp++ = 0;							// Terminate the argv[].

    int status = execv(HEAP_STRING_AS_C_STRING(path), argv);

    RETURN_STATUS_EXCEPT_RAISE_SYSERR_ON_NEGATIVE_STATUS__MAY_HEAPCLEAN(task, status, NULL);
}
Пример #5
0
static Val   read_in_compiled_file_list__may_heapclean   (
    //       =========================================
    //
    Task*          task,
    const char*    compiled_files_to_load_filename,
    int*           return_max_boot_path_len,
    Roots*         extra_roots
){
    // Open given file and read from it the list of
    // filenames of compiled_files to be later loaded.
    // Return them as a Mythryl list of Mythryl strings:

    #define    BUF_LEN	1024		//  "This should be plenty for two numbers."   "640K should be enough for anyone."
    char  buf[ BUF_LEN ];

//  Val*   file_names = NULL;
    char*  name_buf   = NULL;

    int    max_num_boot_files = MAX_NUMBER_OF_BOOT_FILES;
    int    max_boot_path_len  = MAX_LENGTH_FOR_A_BOOTFILE_PATHNAME;

    int    file_count = 0;

    FILE*  list_fd =  open_file( compiled_files_to_load_filename, FALSE );

    fprintf (
        stderr,
        "                    load-compiledfiles.c:   Reading   file          %s\n",
        compiled_files_to_load_filename
    );

    if (log_fd) {
	//
	fprintf (
	    log_fd,
	    "                    load-compiledfiles.c:   Reading   file                    %s\n",
	    compiled_files_to_load_filename
	);
    }

    Val  file_list = LIST_NIL;			Roots roots1 = { &file_list, extra_roots };

    if (list_fd) {

        // Read header:
        //
        for (;;) {
	    //
	    if (!fgets (buf, BUF_LEN, list_fd)) {
                die (
                    "compiled_files_to_load file \"%s\" ends before end-of-header (first empty line)",
                    compiled_files_to_load_filename
                );
            }

	    {    char* p = buf;
                 while (*p == ' ' || *p == '\t')   ++p;		// Skip leading whitespace.

		if (p[0] == '\n')   break;			// Header ends at first empty line.

		if (p[0] == '#')   continue;			// Ignore comment lines.

                if (strstr( p,"FILES=") == p) {
		    //
		    max_num_boot_files = strtoul(p+6, NULL, 0);
                    continue;
                }

                if (strstr(p,"MAX_LINE_LENGTH=") == p) {
		    //
		    max_boot_path_len  = strtoul(p+16, NULL, 0) +2;
                    continue;
                }

                die (
                    "compiled_files_to_load file \"%s\" contains unrecognized header line \"%s\"",
                    compiled_files_to_load_filename,
                    p
                );
	    }
        }

        if (max_num_boot_files < 0)  {
	    //
            die("compiled_files_to_load file \"%s\" contains negative files count?! (%d)",
                compiled_files_to_load_filename,
                max_num_boot_files
            );
        } 

        if (max_boot_path_len  < 0) {
	    //
            die("compiled_file_to_load file \"%s\" contains negative boot path len?! (%d)",
                compiled_files_to_load_filename,
                max_boot_path_len
            );
        }


	*return_max_boot_path_len =   max_boot_path_len;		// Tell the calling function.

	if (!(name_buf = MALLOC( max_boot_path_len ))) {
	    //
	    die ("unable to allocate space for .compiled file filenames");
        }

//	if (!(file_names = MALLOC( max_num_boot_files * sizeof(char*) ))) {
//	    //
//	    die ("Unable to allocate space for compiledfiles-to-load name table");
//        }

        // Read in the file names, converting them to
	// Mythryl strings and saving them in a list:
        //
	while (fgets( name_buf, max_boot_path_len, list_fd )) {

	    // Skip leading whitespace:
	    //
	    char* p = name_buf;
            while (*p == ' ' || *p == '\t')   ++p;

	    // Ignore empty lines and comment lines:
	    //
	    if (*p == '\n')   continue;
	    if (*p ==  '#')   continue;

	    // Strip any trailing newline:
	    //
	    {   int j = strlen(p)-1;
		//
	        if (p[j] == '\n') p[j] = '\0';
	    }	

	    if (file_count >= max_num_boot_files)   die ("too many files\n");

	    // If our agegroup0 buffer is more than half full,
	    // empty it by doing a heapcleaning.  This is very
	    // conservative -- which is the way I like it. *grin*
	    //
	    if (agegroup0_freespace_in_bytes( task )
	      < agegroup0_usedspace_in_bytes( task )
	    ){
		call_heapcleaner_with_extra_roots( task,  0, &roots1 );
	    }

	    Val file_name = make_ascii_string_from_c_string__may_heapclean(task, p, &roots1 );

	    file_list = LIST_CONS(task, file_name, file_list);
	}

	if (name_buf)    FREE( name_buf );

	fclose( list_fd );
    }


    // Reverse filename list (to restore
    // original order) and return it:
    //
    {   Val file_list2 = LIST_NIL;			Roots roots2 = { &file_list2, &roots1 };
	//
	for (; file_list != LIST_NIL;  file_list = LIST_TAIL(file_list)) {
	    //
	    Val file_name = LIST_HEAD(file_list);
	    //
	    file_list2 = LIST_CONS(task, file_name, file_list2);

	    // Again, if our agegroup0 buffer is more than
	    // half full, empty it by doing a heapcleaning:
	    //
	    if (agegroup0_freespace_in_bytes( task )
	      < agegroup0_usedspace_in_bytes( task )
	    ){
		call_heapcleaner_with_extra_roots( task,  0, &roots2 );
	    }
	}

	return file_list2;
    }
}
Пример #6
0
void   load_compiled_files__may_heapclean   (
    // ==================================
    //
    const char*         compiled_files_to_load_filename,
    Heapcleaner_Args*   heap_parameters,				// See   struct cleaner_args   in   src/c/h/heap.h
    Roots*              extra_roots
){
    // Load into the runtime heap all the .compiled files
    // listed one per line in given compiled_files_to_load file:
    //
    // This function is called from exactly one spot, in
    //
    //     src/c/main/runtime-main.c

    int    max_boot_path_len;
    char*  filename_buf;

    int    seen_runtime_package_picklehash = FALSE;			// FALSE until we see the picklehash naming our runtime.

    open_logfile();

    Task* task
	=
	make_task(							// make_task					def in   src/c/main/runtime-state.c
	    TRUE,							// is_boot
	    heap_parameters
	);


    // Set up handlers for ^C, divide-by-zero,
    // integer overflow etc:
    //
    set_up_fault_handlers ();						// set_up_fault_handlers			def in   src/c/machine-dependent/posix-arithmetic-trap-handlers.c
									// set_up_fault_handlers			def in   src/c/machine-dependent/win32-fault.c
									// set_up_fault_handlers			def in   src/c/machine-dependent/cygwin-fault.c	

    // Set up RunVec in CStruct in
    //
    //     runtime_package__global.
    //
    // This constitutes an ersatz exports list implementing
    //
    //     src/lib/core/init/runtime.api
    // 
    // which we will later substitute for the (useless) code from
    //
    //     src/lib/core/init/runtime.pkg
    //
    // thus providing access to critical assembly fns including
    //
    //     find_cfun
    //
    // implemented in one of
    //
    //     src/c/machine-dependent/prim.sparc32.asm
    //     src/c/machine-dependent/prim.pwrpc32.asm
    //     src/c/machine-dependent/prim.intel32.asm
    //     src/c/machine-dependent/prim.intel32.masm
    //
    construct_runtime_package__global( task );				// construct_runtime_package__global	def in   src/c/main/construct-runtime-package.c

    // Construct the list of files to be loaded:
    //
    Val compiled_file_list
	=
	read_in_compiled_file_list__may_heapclean (
	    task,
            compiled_files_to_load_filename,
            &max_boot_path_len,
	    extra_roots
	);

    Roots roots1 = { &compiled_file_list, extra_roots };

    // This space is ultimately wasted:           XXX BUGGO FIXME
    //
    if (! (filename_buf = MALLOC( max_boot_path_len ))) {
	//
	die ("unable to allocate space for boot file names");
    }

    // Load all requested compiled_files into the heap:
    //
    while (compiled_file_list != LIST_NIL) {
	//
        char* filename =  filename_buf;

        // Need to make a copy of the filename because
        // load_compiled_file__may_heapclean is going to scribble into it:
        //
	strcpy( filename_buf, HEAP_STRING_AS_C_STRING( LIST_HEAD( compiled_file_list )));
       
	compiled_file_list = LIST_TAIL( compiled_file_list );		// Remove above filename from list of files to process.

	// If 'filename' does not begin with "RUNTIME_PACKAGE_PICKLEHASH=" ...
	//
	if (strstr(filename,"RUNTIME_PACKAGE_PICKLEHASH=") != filename) {
	    //
	    // ... then we can load it normally:
	    //
	    load_compiled_file__may_heapclean( task, filename, &roots1 );

	} else {

	    // We're processing the
            //
            //     RUNTIME_PACKAGE_PICKLEHASH=...
            //
            // set up for us by
            //
            //     src/app/makelib/mythryl-compiler-compiler/find-set-of-compiledfiles-for-executable.pkg

	    while (*filename++ != '=');   		// Step over "RUNTIME_PACKAGE_PICKLEHASH=" prefix.

	    if (seen_runtime_package_picklehash) {
		//
                if (log_fd) fclose( log_fd );

		die ("Runtime system picklehash registered more than once!\n");
		exit(1);								// Just for gcc's sake -- cannot exectute.

	    }

	    // Most parts of the Mythryl implementation treat the C-coded
	    // runtime functions as being just like library functions
	    // coded in Mythryl -- to avoid special cases, we go to great
	    // lengths to hide the differences.
	    //
	    // But this is one of the places where the charade breaks
	    // down -- there isn't actually any (useful) .compiled file
	    // corresponding to the runtime picklehash:  Instead, we
	    // must link runtime calls directly down into our C code.
	    //
	    // For more info, see the comments in
	    //     src/lib/core/init/runtime.pkg
	    //
	    // So here we implement some of that special handling:

	    // Register the runtime system under the given picklehash:
	    //
	    Picklehash picklehash;

	    int  l = strlen( filename );

	    for (int i = 0;   i < PICKLEHASH_BYTES;   i++) {
	        //
		int i2 = 2 * i;
		if (i2 + 1 < l) {
		    int c1 = filename[i2+0];
		    int c2 = filename[i2+1];
		    picklehash.bytes[i] = (hex(c1) << 4) + hex(c2);
		}
	    }
	    fprintf(
		log_fd ? log_fd : stderr,
		"\n                    load-compiledfiles.c:   Runtime system picklehash is      %s\n\n",
		filename
	    );

	    register_compiled_file_exports__may_heapclean( task, &picklehash, runtime_package__global, &roots1 );

	    seen_runtime_package_picklehash = TRUE;							// Make sure that we register the runtime system picklehash only once.
	}
    }

    if (log_fd)   fclose( log_fd );
}													// load_compiled_files__may_heapclean
Val   make_package_literals_via_bytecode_interpreter   (Task* task,   Unt8* bytecode_vector,   int bytecode_vector_length_in_bytes)   {
    //==============
    //
    // NOTE: We allocate all of the chunks in agegroup 1,
    // but allocate the vector of literals in agegroup0.
    //
    // This fn gets exported to the Mythryl level as
    //
    //     make_package_literals_via_bytecode_interpreter
    // in
    //     src/lib/compiler/execution/code-segments/code-segment.pkg
    // via
    //     src/c/lib/heap/make-package-literals-via-bytecode-interpreter.c
    //
    // Our ultimate invocation is in
    //
    //     src/lib/compiler/execution/main/execute.pkg


    int pc = 0;

    // Check that sufficient space is available for the
    // literal chunk that we are about to allocate.
    // Note that the cons cell has already been accounted
    // for in space_available (but not in space_needed).
    //
    #define GC_CHECK										\
	do {											\
	    if (space_needed > space_available							\
            &&  need_to_call_heapcleaner( task, space_needed + LIST_CONS_CELL_BYTESIZE)		\
            ){											\
		call_heapcleaner_with_extra_roots (task, 0, (Val *)&bytecode_vector, &stk, NULL);	\
		space_available = 0;								\
												\
	    } else {										\
												\
		space_available -= space_needed;						\
	    }											\
	} while (0)

    #ifdef DEBUG_LITERALS
	debug_say("make_package_literals_via_bytecode_interpreter: bytecode_vector = %#x, bytecode_vector_length_in_bytes = %d\n", bytecode_vector, bytecode_vector_length_in_bytes);
    #endif

    if (bytecode_vector_length_in_bytes <= 8)   return HEAP_NIL;

    Val_Sized_Unt  magic
	=
	GET32(bytecode_vector);   pc += 4;

    Val_Sized_Unt  max_depth							/* This variable is currently unused, so suppress 'unused var' compiler warning: */   __attribute__((unused))
	=
	GET32(bytecode_vector);   pc += 4;

    if (magic != V1_MAGIC) {
	die("bogus literal magic number %#x", magic);
    }

    Val	stk = HEAP_NIL;

    int space_available = 0;

    for (;;) {
	//
	ASSERT(pc < bytecode_vector_length_in_bytes);

	space_available -= LIST_CONS_CELL_BYTESIZE;	// Space for stack cons cell.

	if (space_available < ONE_K_BINARY) {
	    //
	    if (need_to_call_heapcleaner(task, 64*ONE_K_BINARY)) {
		//
		call_heapcleaner_with_extra_roots (task, 0, (Val *)&bytecode_vector, &stk, NULL);
            }
	    space_available = 64*ONE_K_BINARY;
	}


	switch (bytecode_vector[ pc++ ]) {
	    //
	case I_INT:
	    {	int i = GET32(bytecode_vector);	pc += 4;

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: INT(%d)\n", pc-5, i);
		#endif

		LIST_CONS(task, stk, TAGGED_INT_FROM_C_INT(i), stk);
	    }
	    break;

	case I_RAW32:
	    {
		int i = GET32(bytecode_vector);	pc += 4;

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: RAW32[%d]\n", pc-5, i);
		#endif

		Val               result;
		INT1_ALLOC(task, result, i);

		LIST_CONS(task, stk, result, stk);
		space_available -= 2*WORD_BYTESIZE;
	    }
	    break;

	case I_RAW32L:
	    {
		int n = GET32(bytecode_vector);	pc += 4;

		#ifdef DEBUG_LITERALS
		debug_say("[%2d]: RAW32L(%d) [...]\n", pc-5, n);
		#endif

		ASSERT(n > 0);

		int space_needed = 4*(n+1);
		GC_CHECK;

		LIB7_AllocWrite (task, 0, MAKE_TAGWORD(n, FOUR_BYTE_ALIGNED_NONPOINTER_DATA_BTAG));

		for (int j = 1;  j <= n;  j++) {
		    //
		    int i = GET32(bytecode_vector);	pc += 4;

		    LIB7_AllocWrite (task, j, (Val)i);
		}

		Val result =  LIB7_Alloc(task, n );

		LIST_CONS(task, stk, result, stk);
	    }
	    break;

	case I_RAW64:
	    {
		double d = get_double(&(bytecode_vector[pc]));	pc += 8;

		Val	           result;
		REAL64_ALLOC(task, result, d);

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: RAW64[%f] @ %#x\n", pc-5, d, result);
		#endif

		LIST_CONS(task, stk, result, stk);

		space_available -= 4*WORD_BYTESIZE;		// Extra 4 bytes for alignment padding.
	    }
	    break;

	case I_RAW64L:
	    {
		int n = GET32(bytecode_vector);	pc += 4;

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: RAW64L(%d) [...]\n", pc-5, n);
		#endif

		ASSERT(n > 0);

		int space_needed = 8*(n+1);
		GC_CHECK;

		#ifdef ALIGN_FLOAT64S
		    // Force FLOAT64_BYTESIZE alignment (descriptor is off by one word)
		    //
		    task->heap_allocation_pointer = (Val*)((Punt)(task->heap_allocation_pointer) | WORD_BYTESIZE);
		#endif

		int j = 2*n;							// Number of words.

		LIB7_AllocWrite (task, 0, MAKE_TAGWORD(j, EIGHT_BYTE_ALIGNED_NONPOINTER_DATA_BTAG));

		Val result =  LIB7_Alloc(task, j );

		for (int j = 0;  j < n;  j++) {
		    //
		    PTR_CAST(double*, result)[j] = get_double(&(bytecode_vector[pc]));	pc += 8;
		}
		LIST_CONS(task, stk, result, stk);
	    }
	    break;

	case I_STR:
	    {
		int n = GET32(bytecode_vector);		pc += 4;

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: STR(%d) [...]", pc-5, n);
		#endif

		if (n == 0) {
		    #ifdef DEBUG_LITERALS
			debug_say("\n");
		    #endif

		    LIST_CONS(task, stk, ZERO_LENGTH_STRING__GLOBAL, stk);

		    break;
		}

		int j = BYTES_TO_WORDS(n+1);								// '+1' to include space for '\0'.

		// The space request includes space for the data-chunk header word and
		// the sequence header chunk.
		//
		int space_needed = WORD_BYTESIZE*(j+1+3);
		GC_CHECK;

		// Allocate the data chunk:
		//
		LIB7_AllocWrite(task, 0, MAKE_TAGWORD(j, FOUR_BYTE_ALIGNED_NONPOINTER_DATA_BTAG));
		LIB7_AllocWrite (task, j, 0);								// So word-by-word string equality works.

		Val result = LIB7_Alloc (task, j);

		#ifdef DEBUG_LITERALS
		    debug_say(" @ %#x (%d words)\n", result, j);
		#endif
		memcpy (PTR_CAST(void*, result), &bytecode_vector[pc], n);		pc += n;

		// Allocate the header chunk:
		//
		SEQHDR_ALLOC(task, result, STRING_TAGWORD, result, n);

		// Push on stack:
		//
		LIST_CONS(task, stk, result, stk);
	    }
	    break;

	case I_LIT:
	    {
		int n = GET32(bytecode_vector);	pc += 4;

		Val result = stk;

		for (int j = 0;  j < n;  j++) {
		    //
		    result = LIST_TAIL(result);
		}

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: LIT(%d) = %#x\n", pc-5, n, LIST_HEAD(result));
		#endif

		LIST_CONS(task, stk, LIST_HEAD(result), stk);
	    }
	    break;

	  case I_VECTOR:
	    {
		int n = GET32(bytecode_vector);	pc += 4;

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: VECTOR(%d) [", pc-5, n);
		#endif

		if (n == 0) {
		    #ifdef DEBUG_LITERALS
			debug_say("]\n");
		    #endif
		    LIST_CONS(task, stk, ZERO_LENGTH_VECTOR__GLOBAL, stk);
		    break;
		}

		// The space request includes space
		// for the data-chunk header word and
		// the sequence header chunk.
		//
		int space_needed = WORD_BYTESIZE*(n+1+3);
		GC_CHECK;

		// Allocate the data chunk:
		//
		LIB7_AllocWrite(task, 0, MAKE_TAGWORD(n, RO_VECTOR_DATA_BTAG));

		// Top of stack is last element in vector:
		//
		for (int j = n;  j > 0;  j--) {
		    //
		    LIB7_AllocWrite(task, j, LIST_HEAD(stk));

		    stk = LIST_TAIL(stk);
		}

		Val result =  LIB7_Alloc(task, n );

		// Allocate the header chunk:
		//
		SEQHDR_ALLOC(task, result, TYPEAGNOSTIC_RO_VECTOR_TAGWORD, result, n);

		#ifdef DEBUG_LITERALS
		    debug_say("...] @ %#x\n", result);
		#endif

		LIST_CONS(task, stk, result, stk);
	    }
	    break;

	case I_RECORD:
	    {
		int n = GET32(bytecode_vector);	pc += 4;

		#ifdef DEBUG_LITERALS
		    debug_say("[%2d]: RECORD(%d) [", pc-5, n);
		#endif

		if (n == 0) {
		    #ifdef DEBUG_LITERALS
			debug_say("]\n");
		    #endif

		    LIST_CONS(task, stk, HEAP_VOID, stk);
		    break;

		} else {

		    int space_needed = 4*(n+1);
		    GC_CHECK;

		    LIB7_AllocWrite(task, 0, MAKE_TAGWORD(n, PAIRS_AND_RECORDS_BTAG));
		}

		// Top of stack is last element in record:
		//
		for (int j = n;  j > 0;  j--) {
		    //
		    LIB7_AllocWrite(task, j, LIST_HEAD(stk));

		    stk = LIST_TAIL(stk);
		}

		Val result = LIB7_Alloc(task, n );

		#ifdef DEBUG_LITERALS
		    debug_say("...] @ %#x\n", result);
		#endif

		LIST_CONS(task, stk, result, stk);
	    }
	    break;

	case I_RETURN:
	    ASSERT(pc == bytecode_vector_length_in_bytes);

	    #ifdef DEBUG_LITERALS
	        debug_say("[%2d]: RETURN(%#x)\n", pc-5, LIST_HEAD(stk));
	    #endif

	    return  LIST_HEAD( stk );
	    break;

	default:
	    die ("bogus literal opcode #%x @ %d", bytecode_vector[pc-1], pc-1);
	}								// switch
    }									// while
}									// fun make_package_literals_via_bytecode_interpreter