CS143-Lab/lib/trap.handler
2023-03-16 15:55:37 +00:00

2564 lines
78 KiB
Plaintext

# SPIM S20 MIPS simulator.
#
# Modified trap handler for COOL runtime.
#
# 2/01/95 Carleton Miyamoto
# 8/19/94 Manuel Fahndrich
#
# $Log: trap.handler,v $
# Revision 1.9 2004/04/21 02:13:53 bec
# Merged-in changes to print the class of the exception
#
# Revision 1.8 2004/04/20 03:04:02 bec
# Merged-in handler for exceptions. This will not affect code generators
# that do not implement exceptions.
#
# Revision 1.7 2004/01/22 03:06:34 jcondit
# merged matth's changes (gc init msgs) from sp03
#
# Revision 1.6 2003/07/20 18:52:58 matth
# fixed a bug in the comments of Object.copy: t3 and t4 are clobberred
#
# Revision 1.5 2003/04/12 02:36:54 matth
# updated trap.handler for Spim 6.5. (Don't use andi with 32-bit immediates)
#
# Revision 1.4 2001/04/23 09:47:32 smcpeak
# fixed an off-by-1 bug in stack tracing code
#
# Revision 1.3 2001/03/23 07:30:53 smcpeak
# fixed _GenGC_Assign so it doesn't clobber $a0
#
# Revision 1.2 1996/10/12 18:54:35 darcy
# AFter Merge versions.
#
# Revision 1.1.1.1.4.1 1996/09/17 08:21:39 aiken
# new Makefile, new results
#
#
#
# - updated instructions
# - cleanup of Makefile + new parameters
# - new VERSION file
#
# cleanup of Makefile; removed -static
#
# fixed a bug in PA2 assignment skeleton
#
# paramaterization
#
# updated skeletons to pass ostreams by ref
# paramaterized makefile
#
#
# pass ostreams by ref in SKEL
# parameterized Makefile
#
# new parameters
#
# new parameters
#
# just copied, not actually updated
#
# Revision 1.1.1.1 1996/07/12 06:48:08 aiken
# Imported sources
#
# Revision 2.3 1996/05/31 17:55:22 aiken
# added copyright notice
#
# Revision 2.2 1996/05/28 22:02:38 aiken
# fixed spelling errors,
# brought comments up to date
#
# Revision 2.1 1995/11/08 01:14:41 dgay
# Add a _MemMgr_Test function that calls the garbage collector if the
# global _MemMgr_TEST is non-zero (this global is generated by coolc
# when the -t option is used).
# _MemMgr_Test is called on every memory allocation, i.e. in Object.copy,
# String.concat, String.substr, IO.in_string, IO.in_int
#
# Revision 2.0 1995/09/26 01:26:13 dgay
# - Merged changes between revisions 1.12 and 1.13 of trap handler into
# trap handler for GC from miyamoto.
# - Fixed bug in generational GC: didn't handle assignments properly
# (registers were trashed)
# - Changed method of identifying pointers on stack:
# no tags pushed, simply checks that values are even and are in the
# correct address range. Aborts if eyecatcher missing.
# - Added _gc_check primitive to check the presence of an eyecatcher
# on an object.
# - Removed the Stop & Copy GC.
#
# Revision 2.2 1995/05/17 20:15:41 miyamoto
# - The "NoGC" garbage collector. This collector does nothing but
# expand the heap when more memory is required.
# - support for multiple garbage collectors added.
# - "_MemMgr_*" functions give an consistent view of the garbage
# collectors. It uses the addresses stored in "_MemMgr_INITIALIZER"
# and "_MemMgr_COLLECTOR" to run the correct garbage collector.
# - "_MemMgr_INITIALIZER" holds the address of the initialization
# function for the garbage collector. This is defined in the
# program file (*.s).
# - "_MemMgr_COLLECTOR" holds the address of the garbage collection
# function. This is defined in the program file (*.s).
# - MemMgr_REG_MASK is used to determine which registers get auto-
# updated.
# - "_*GC_*Alloc" functions were removed.
# - "*_REG_MASK" variables removed.
#
# Revision 2.1 1995/03/13 15:58:32 miyamoto
# - GenGC "Generational" garbage collector
# - Converted all calls to "_SncGC_*" to their equivalent "_GenGC_*"
# - Created constants:
# GenGC_HDR*: Information in the GenGC header
# GenGC_REG_MASK: Register mask for auto update
# GenGC_ARU_MASK: All possible registers that can be auto updated
# GenGC_HEAPEXPGRAN: Granularity of any heap expansion, heap is
# always expanded in multiples of 2^GenGC_HEAPEXPGRAN
# GenGC_OLDRATIO: Ratio of size of the old area to usable heap size
# - Created GenGC error messages in the data seg (_GenGC_*)
# - Object.copy: displays error message on an invalid object size
#
# Revision 2.0 1995/02/16 17:32:38 miyamoto
# - SncGC "Stop and Copy" garbage collector
# - Created some constants:
# obj_eyecatch: offset from object pointer to the eyecatcher
# obj_disp: offset from object pointer to the dispatch table
# obj_attr: offset from object pointer to the attributes
# str_maxsize: the maximum length of a string
# SncGC_REG_MASK: Register mask for auto update
# SncGC_ARU_MASK: All possible registers that can be auto updated
# SncGC_EXPANDSIZE: Size to expand heap when necessary (bytes)
# SncGC_INFOSIZE: Size of SncGC header in the heap (bytes)
# - Created some SncGC error messages in the data seg (_SncGC_*)
# - Renamed labels in equality_test to contain a leading underscore
# to avoid any conflicts which may arise
# - Functions changed to accomodate SncGC:
# __start: pushs Main object on the stack and calls "_SncGC_Init"
# Object.copy: calls "_SncGC_Alloc" to allocate memory
# IO.in_string, String.concat, String.substr:
# uses "_SncGC_QAlloc" to verify allocation of memory
# - Functions changed to accomodate stack flags:
# Object.copy, IO.out_string, IO.out_int, IO.in_int, IO.in_string
# String.concat, String.substr
# - __start now sets $s0 to the self pointer of the Main object
#
# *****
# Warning (no changes made): String.substr trashes regs $t3 $t4
# *****
#
# Revision 1.12 1994/11/15 03:33:34 manuel
# substr method didn't allow taking the empty substr at the end of a
# string
#
# Revision 1.11 1994/11/14 21:41:23 manuel
# Comment for equality_test contained a type: arguments are in $t1 and
# $t2
#
# Revision 1.10 1994/10/26 02:31:50 manuel
# Added more comments.
#
# Revision 1.9 1994/08/31 02:04:31 manuel
# Fixed an error in the in_string code: Reading from EOF, the system
# returns 0 characters. We test for this and return a single '\n'. The
# code can therefore recognize EOF.
# The last line of a file must be terminated by a newline, otherwise
# spim gets confused and returns the entire buffer!
#
# Revision 1.8 1994/08/28 02:21:46 manuel
# - Fixed typo in system message
#
# Revision 1.7 1994/08/27 08:53:35 manuel
# - Added an .align at end of data segment to be safe. Cgen should emit
# one.
#
# Revision 1.6 1994/08/27 08:37:49 manuel
# - Added string primitives
#
# Revision 1.5 1994/08/27 04:42:25 manuel
# - Adapted code to handle String Class containing an Int object slot
# for the string size.
#
# Revision 1.4 1994/08/27 02:01:43 manuel
# - Fixed typos
#
# Revision 1.2 1994/08/27 00:41:01 manuel
# - Changed string object representation to two slots. The first is a 32
# bit slot indicating the string length, the second is a variable
# sized slot containing the actual null terminated string.
# - Fixed a bug in the in_string function which set obj_size to
# 4*obj_size
# - Added constants for field offsets
#
#
# SPIM is distributed under the following conditions:
#
# You may make copies of SPIM for your own use and modify those copies.
#
# All copies of SPIM must retain my name and copyright notice.
#
# You may not sell SPIM or distributed SPIM in conjunction with a commerical
# product or service without the expressed written consent of James Larus.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE.
#
# Define the exception handling code. This must go first!
.kdata
__m1_: .asciiz " Exception "
__m2_: .asciiz " Execution aborted\n"
__e0_: .asciiz " [Interrupt] "
__e1_: .asciiz ""
__e2_: .asciiz ""
__e3_: .asciiz ""
__e4_: .asciiz " [Unaligned address in inst/data fetch] "
__e5_: .asciiz " [Unaligned address in store] "
__e6_: .asciiz " [Bad address in text read] "
__e7_: .asciiz " [Bad address in data/stack read] "
__e8_: .asciiz " [Error in syscall] "
__e9_: .asciiz " [Breakpoint/Division by 0] "
__e10_: .asciiz " [Reserved instruction] "
__e11_: .asciiz ""
__e12_: .asciiz " [Arithmetic overflow] "
__e13_: .asciiz " [Inexact floating point result] "
__e14_: .asciiz " [Invalid floating point result] "
__e15_: .asciiz " [Divide by 0] "
__e16_: .asciiz " [Floating point overflow] "
__e17_: .asciiz " [Floating point underflow] "
__excp: .word __e0_,__e1_,__e2_,__e3_,__e4_,__e5_,__e6_,__e7_,__e8_,__e9_
.word __e10_,__e11_,__e12_,__e13_,__e14_,__e15_,__e16_,__e17_
s1: .word 0
s2: .word 0
.ktext 0x80000080
.set noat
# Because we are running in the kernel, we can use $k0/$k1 without
# saving their old values.
move $at $k1 # Save $at
.set at
sw $v0 s1 # Not re-entrent and we can't trust $sp
sw $a0 s2
mfc0 $k0 $13 # Cause
sgt $v0 $k0 0x44 # ignore interrupt exceptions
bgtz $v0 ret
addu $0 $0 0
li $v0 4 # syscall 4 (print_str)
la $a0 __m1_
syscall
li $v0 1 # syscall 1 (print_int)
srl $a0 $k0 2 # shift Cause reg
syscall
li $v0 4 # syscall 4 (print_str)
lw $a0 __excp($k0)
syscall
li $v0 4
la $a0 __m2_
syscall
li $v0 10 # Exit upon all exceptions
syscall # syscall 10 (exit)
ret: lw $v0 s1
lw $a0 s2
mfc0 $k0 $14 # EPC
.set noat
move $k1 $at # Restore $at
.set at
rfe # Return from exception handler
addiu $k0 $k0 4 # Return to next instruction
jr $k0
#
# The code above this line is from spim. Below this line are
# the MIPS assembly routines implementing the Cool runtime system.
# This code is Copyright (c) 1995,1996 The Regents of the University
# of California. All rights reserved. See copyright.h for
# limitation of liability and disclaimer of warranty provisions.
#
#
# Functions that return to the cool caller, should preserve $s0-$s7
#
# $s7 is reserved as the limit pointer.
# $gp is the heap pointer (points to the next unused word)
# should never be handled by the generated code!!!
# $sp is the stack pointer
# $ra contains the return address
#
# $v0, $v1, $t0, $t1, $t2, $a0, $a1, $a2 are scratch registers
# (i.e. caller cannot assume that they remain unchanged)
#
#
# Standard startup code. Invoke the routine main with no arguments.
#
.data
_abort_msg: .asciiz "Abort called from class "
_colon_msg: .asciiz ":"
_dispatch_msg: .asciiz ": Dispatch to void.\n"
_cabort_msg: .asciiz "No match in case statement for Class "
_cabort_msg2: .asciiz "Match on void in case statement.\n"
_nl: .asciiz "\n"
_term_msg: .asciiz "COOL program successfully executed\n"
_sabort_msg1: .asciiz "Index to substr is negative\n"
_sabort_msg2: .asciiz "Index to substr is too big\n"
_sabort_msg3: .asciiz "Length to substr too long\n"
_sabort_msg4: .asciiz "Length to substr is negative\n"
_sabort_msg: .asciiz "Execution aborted.\n"
_objcopy_msg: .asciiz "Object.copy: Invalid object size.\n"
_gc_abort_msg: .asciiz "GC bug!\n"
# Exception Handler Message:
_uncaught_msg1: .asciiz "Uncaught Exception of Class "
_uncaught_msg2: .asciiz " thrown. COOL program aborted.\n"
_exception_handler:
.word __exception
# Stack overflow handler message:
_stack_overflow_msg: .asciiz " Stack overflow detected, COOL program aborted\n"
#
# Messages for the GenGC garbage collector
#
_GenGC_INITERROR: .asciiz "GenGC: Unable to initialize the garbage collector.\n"
_GenGC_COLLECT: .asciiz "Garbage collecting ...\n"
_GenGC_Major: .asciiz "Major ...\n"
_GenGC_Minor: .asciiz "Minor ...\n"
#_GenGC_COLLECT: .asciiz ""
_GenGC_MINORERROR: .asciiz "GenGC: Error during minor garbage collection.\n"
_GenGC_MAJORERROR: .asciiz "GenGC: Error during major garbage collection.\n"
_GenGC_Init_test_msg: .asciiz "GenGC initialized in test mode.\n"
_GenGC_Init_msg: .asciiz "GenGC initialized.\n"
#
# Messages for the NoGC garabge collector
#
_NoGC_COLLECT: .asciiz "Increasing heap...\n"
#_NoGC_COLLECT: .asciiz ""
.align 2
#
# Define some constants
#
obj_eyecatch=-4 # Unique id to verify any object
obj_tag=0
obj_size=4
obj_disp=8
obj_attr=12
int_slot=12
bool_slot=12
str_size=12 # This is a pointer to an Int object!!!
str_field=16 # The beginning of the ascii sequence
str_maxsize=1026 # the maximum string length
#
# The REG mask tells the garbage collector which register(s) it
# should automatically update on a garbage collection. Note that
# this is (ANDed) with the ARU mask before the garbage collector
# reads it. Only the registers specified in the garbage collector's
# ARU mask can be automatically updated.
#
# BITS----------------------------
# 3 2 1 0
# 10987654321098765432109876543210
# --------------------------------
#
# 00000000011111110000000000000000 <- initial Register (REG) mask
# +--++--++--++--++--++--++--++--+ $s0-$s6
# 0 0 7 F 0 0 0 0 ($16-$22)
#
MemMgr_REG_MASK=0x007F0000
.text
.globl __exception
# Exception Message
__exception: # $a0 contains case expression obj.
move $s0 $a0 # save the expression object
la $a0 _uncaught_msg1
li $v0 4
syscall # print message
la $t1 class_nameTab
lw $v0 obj_tag($s0) # Get object tag
sll $v0 $v0 2 # *4
addu $t1 $t1 $v0
lw $t1 0($t1) # Load class name string obj.
addiu $a0 $t1 str_field # Adjust to beginning of str
li $v0 4 # print_str
syscall
la $a0 _uncaught_msg2
li $v0 4 # print_str
syscall
li $v0 10
syscall # Exit
.globl _stack_overflow_abort
# Stack Overflow Message
_stack_overflow_abort:
la $a0 _stack_overflow_msg
li $v0 4
syscall # print message
li $v0 10
syscall # Exit
.globl __start
__start:
li $v0 9
move $a0 $zero
syscall # sbrk
move $a0 $sp # initialize the garbage collector
li $a1 MemMgr_REG_MASK
move $a2 $v0
jal _MemMgr_Init # sets $gp and $s7 (limit)
la $t9 _exception_handler # Exception: Set uncaught Exception Address
la $a0 Main_protObj # create the Main object
jal Object.copy # Call copy
addiu $sp $sp -4
sw $a0 4($sp) # save the Main object on the stack
move $s0 $a0 # set $s0 to point to self
jal Main_init # initialize the Main object
jal Main.main # Invoke main method
.globl __main_return
__main_return: # where we return after the call to Main.main
addiu $sp $sp 4 # restore the stack
la $a0 _term_msg # show terminal message
li $v0 4
syscall
li $v0 10
syscall # syscall 10 (exit)
#
# Polymorphic equality testing function:
# Two objects are equal if they are
# - identical (pointer equality, inlined in code)
# - have same tag and are of type BOOL,STRING,INT and contain the
# same data
#
# INPUT: The two objects are passed in $t1 and $t2
# OUTPUT: Initial value of $a0, if the objects are equal
# Initial value of $a1, otherwise
#
# The tags for Int,Bool,String are found in the global locations
# _int_tag, _bool_tag, _string_tag, which are initialized by the
# data part of the generated code. This removes a consistency problem
# between this file and the generated code.
#
.globl equality_test
equality_test: # ops in $t1 $t2
# true in A0, false in A1
# assume $t1, $t2 are not equal
beq $t1 $zero _eq_false # $t2 can't also be void
beq $t2 $zero _eq_false # $t1 can't also be void
lw $v0 obj_tag($t1) # get tags
lw $v1 obj_tag($t2)
bne $v1 $v0 _eq_false # compare tags
lw $a2 _int_tag # load int tag
beq $v1 $a2 _eq_int # Integers
lw $a2 _bool_tag # load bool tag
beq $v1 $a2 _eq_int # Booleans
lw $a2 _string_tag # load string tag
bne $v1 $a2 _eq_false # Not a primitive type
_eq_str: # handle strings
lw $v0, str_size($t1) # get string size objs
lw $v1, str_size($t2)
lw $v0, int_slot($v0) # get string sizes
lw $v1, int_slot($v1)
bne $v1 $v0 _eq_false
beqz $v1 _eq_true # 0 length strings are equal
add $t1 str_field # Point to start of string
add $t2 str_field
move $t0 $v0 # Keep string length as counter
_eq_l1:
lbu $v0,0($t1) # get char
add $t1 1
lbu $v1,0($t2)
add $t2 1
bne $v1 $v0 _eq_false
addiu $t0 $t0 -1 # Decrement counter
bnez $t0 _eq_l1
b _eq_true # end of strings
_eq_int: # handles booleans and ints
lw $v0,int_slot($t1) # load values
lw $v1,int_slot($t2)
bne $v1 $v0 _eq_false
_eq_true:
jr $ra # return true
_eq_false:
move $a0 $a1 # move false into accumulator
jr $ra
#
# _dispatch_abort
#
# filename in $a0
# line number in $t1
#
# Prints error message and exits.
# Called on dispatch to void.
#
.globl _dispatch_abort
_dispatch_abort:
sw $t1 0($sp) # save line number
addiu $sp $sp -4
addiu $a0 $a0 str_field # adjust to beginning of string
li $v0 4
syscall # print file name
la $a0 _colon_msg
li $v0 4
syscall # print ":"
lw $a0 4($sp) #
li $v0 1
syscall # print line number
li $v0 4
la $a0 _dispatch_msg
syscall # print dispatch-to-void message
li $v0 10
syscall # exit
#
# _case_abort2
#
# filename in $a0
# line number in $t1
#
# Prints error message and exits.
# Called on case on void.
#
.globl _case_abort2
_case_abort2:
sw $t1 0($sp) # save line number
addiu $sp $sp -4
addiu $a0 $a0 str_field # adjust to beginning of string
li $v0 4
syscall # print file name
la $a0 _colon_msg
li $v0 4
syscall # print ":"
lw $a0 4($sp) #
li $v0 1
syscall # print line number
li $v0 4
la $a0 _cabort_msg2
syscall # print case-on-void message
li $v0 10
syscall # exit
#
#
# _case_abort
# Is called when a case statement has no match
#
# INPUT: $a0 contains the object on which the case was
# performed
#
# Does not return!
#
.globl _case_abort
_case_abort: # $a0 contains case expression obj.
move $s0 $a0 # save the expression object
la $a0 _cabort_msg
li $v0 4
syscall # print_str
la $t1 class_nameTab
lw $v0 obj_tag($s0) # Get object tag
sll $v0 $v0 2 # *4
addu $t1 $t1 $v0
lw $t1 0($t1) # Load class name string obj.
addiu $a0 $t1 str_field # Adjust to beginning of str
li $v0 4 # print_str
syscall
la $a0 _nl
li $v0 4 # print_str
syscall
li $v0 10
syscall # Exit
#
# Copy method
#
# Copies an object and returns a pointer to a new object in
# the heap. Note that to increase performance, the stack frame
# is not set up unless it is absolutely needed. As a result,
# the frame is setup just before the call to "_MemMgr_Alloc" and
# is destroyed just after it. The increase in performance
# occurs becuase the calls to "_MemMgr_Alloc" happen very
# infrequently when the heap needs to be garbage collected.
#
# INPUT: $a0: object to be copied to free space in heap
#
# OUTPUT: $a0: points to the newly created copy.
#
# Registers modified:
# $t0, $t1, $t2, $t3, $t4, $v0, $v1, $a0, $a1, $a2, $gp, $s7
#
.globl Object.copy
Object.copy:
addiu $sp $sp -8 # create stack frame
sw $ra 8($sp)
sw $a0 4($sp)
jal _MemMgr_Test # test GC area
lw $a0 4($sp) # get object size
lw $a0 obj_size($a0)
blez $a0 _objcopy_error # check for invalid size
sll $a0 $a0 2 # convert words to bytes
addiu $a0 $a0 4 # account for eyecatcher
jal _MemMgr_Alloc # allocate storage
addiu $a1 $a0 4 # pointer to new object
lw $a0 4($sp) # the self object
lw $ra 8($sp) # restore return address
addiu $sp $sp 8 # remove frame
lw $t0 obj_size($a0) # get size of object
sll $t0 $t0 2 # convert words to bytes
b _objcopy_allocated # get on with the copy
# A faster version of Object.copy, for internal use (does not call
# _MemMgr_Test, and if possible not _MemMgr_Alloc)
_quick_copy:
lw $t0 obj_size($a0) # get size of object to copy
blez $t0 _objcopy_error # check for invalid size
sll $t0 $t0 2 # convert words to bytes
addiu $t1 $t0 4 # account for eyecatcher
add $gp $gp $t1 # allocate memory
sub $a1 $gp $t0 # pointer to new object
blt $gp $s7 _objcopy_allocated # check allocation
_objcopy_allocate:
sub $gp $a1 4 # restore the original $gp
addiu $sp $sp -8 # frame size
sw $ra 8($sp) # save return address
sw $a0 4($sp) # save self
move $a0 $t1 # put bytes to allocate in $a0
jal _MemMgr_Alloc # allocate storage
addiu $a1 $a0 4 # pointer to new object
lw $a0 4($sp) # the self object
lw $ra 8($sp) # restore return address
addiu $sp $sp 8 # remove frame
lw $t0 obj_size($a0) # get size of object
sll $t0 $t0 2 # convert words to bytes
_objcopy_allocated:
addiu $t1 $0 -1
sw $t1 obj_eyecatch($a1) # store eyecatcher
add $t0 $t0 $a0 # find limit of copy
move $t1 $a1 # save source
_objcopy_loop:
lw $v0 0($a0) # copy word
sw $v0 0($t1)
addiu $a0 $a0 4 # update source
addiu $t1 $t1 4 # update destination
bne $a0 $t0 _objcopy_loop # loop
_objcopy_end:
move $a0 $a1 # put new object in $a0
jr $ra # return
_objcopy_error:
la $a0 _objcopy_msg # show error message
li $v0 4
syscall
li $v0 10 # exit
syscall
#
#
# Object.abort
#
# The abort method for the object class (usually inherited by
# all other classes)
#
# INPUT: $a0 contains the object on which abort() was dispatched.
#
.globl Object.abort
Object.abort:
move $s0 $a0 # save self
li $v0 4
la $a0 _abort_msg
syscall # print_str
la $t1 class_nameTab
lw $v0 obj_tag($s0) # Get object tag
sll $v0 $v0 2 # *4
addu $t1 $t1 $v0
lw $t1 0($t1) # Load class name string obj.
addiu $a0 $t1 str_field # Adjust to beginning of str
li $v0 4 # print_str
syscall
la $a0 _nl
li $v0 4
syscall # print new line
li $v0 10
syscall # Exit
#
#
# Object.type_name
#
# INPUT: $a0 object who's class name is desired
# OUTPUT: $a0 reference to class name string object
#
.globl Object.type_name
Object.type_name:
la $t1 class_nameTab
lw $v0 obj_tag($a0) # Get object tag
sll $v0 $v0 2 # *4
addu $t1 $t1 $v0 # index table
lw $a0 0($t1) # Load class name string obj.
jr $ra
#
#
# IO.out_string
#
# Prints out the contents of a string object argument
# which is on top of the stack.
#
# $a0 is preserved!
#
.globl IO.out_string
IO.out_string:
addiu $sp $sp -4
sw $a0 4($sp) # save self
lw $a0 8($sp) # get arg
addiu $a0 $a0 str_field # Adjust to beginning of str
li $v0 4 # print_str
syscall
lw $a0 4($sp) # return self
addiu $sp $sp 8 # pop argument
jr $ra
#
#
# IO.out_int
#
# Prints out the contents of an integer object on top of the
# stack.
#
# $a0 is preserved!
#
.globl IO.out_int
IO.out_int:
addiu $sp $sp -4
sw $a0 4($sp) # save self
lw $a0 8($sp) # get arg
lw $a0 int_slot($a0) # Fetch int
li $v0 1 # print_int
syscall
lw $a0 4($sp) # return self
addiu $sp $sp 8 # pop argument
jr $ra
#
#
# IO.in_int
#
# Returns an integer object read from the terminal in $a0
#
.globl IO.in_int
IO.in_int:
addiu $sp $sp -4
sw $ra 4($sp) # save return address
la $a0 Int_protObj
jal _quick_copy # Call copy
jal Int_init
addiu $sp $sp -4
sw $a0 4($sp) # save new object
li $v0, 5 # read int
syscall
lw $a0 4($sp)
addiu $sp $sp 4
sw $v0 int_slot($a0) # store int read into obj
lw $ra 4($sp)
addiu $sp $sp 4
jr $ra
#
#
# IO.in_string
#
# Returns a string object read from the terminal, removing the
# '\n'
#
# OUTPUT: $a0 the read string object
#
.globl IO.in_string
IO.in_string:
addiu $sp $sp -8
sw $ra 8($sp) # save return address
sw $0 4($sp) # init GC area
jal _MemMgr_Test # test GC area
la $a0 Int_protObj # Int object for string size
jal _quick_copy
jal Int_init
sw $a0 4($sp) # save it
li $a0 str_field # size of string obj. header
addiu $a0 $a0 str_maxsize # max size of string data
jal _MemMgr_QAlloc # make sure enough room
la $a0 String_protObj # make string object
jal _quick_copy
jal String_init
lw $t0 4($sp) # get size object
sw $t0 str_size($a0) # store size object in string
sw $a0 4($sp) # save string object
addiu $gp $gp -4 # overwrite last word
_instr_ok:
li $a1 str_maxsize # largest string to read
move $a0 $gp
li $v0, 8 # read string
syscall
move $t0 $gp # t0 to beginning of string
_instr_find_end:
lb $v0 0($gp)
addiu $gp $gp 1
bnez $v0 _instr_find_end
# $gp points just after the null byte
lb $v0 0($t0) # is first byte '\0'?
bnez $v0 _instr_noteof
# we read nothing. Return '\n' (we don't have '\0'!!!)
add $v0 $zero 10 # load '\n' into $v0
sb $v0 -1($gp)
sb $zero 0($gp) # terminate
addiu $gp $gp 1
b _instr_nonl
_instr_noteof:
# Check if there really is a '\n'
lb $v0 -2($gp)
bne $v0 10 _instr_nonl
# Write '\0' over '\n'
sb $zero -2($gp) # Set end of string where '\n' was
addiu $gp $gp -1 # adjust for '\n'
_instr_nonl:
lw $a0 4($sp) # get pointer to new str obj
lw $t1 str_size($a0) # get pointer to int obj
sub $t0 $gp $a0
subu $t0 str_field # calc actual str size
addiu $t0 -1 # adjust for '\0'
sw $t0 int_slot($t1) # store string size in int obj
addi $gp $gp 3 # was already 1 past '\0'
la $t0 0xfffffffc
and $gp $gp $t0 # word align $gp
sub $t0 $gp $a0 # calc length
srl $t0 $t0 2 # divide by 4
sw $t0 obj_size($a0) # set size field of obj
lw $ra 8($sp) # restore return address
addiu $sp $sp 8
jr $ra # return
#
#
# String.length
# Returns Int Obj with string length of self
#
# INPUT: $a0 the string object
# OUTPUT: $a0 the int object which is the size of the string
#
.globl String.length
String.length:
lw $a0 str_size($a0) # fetch attr
jr $ra # Return
#
# String.concat
#
# Concatenates arg1 onto the end of self and returns a pointer
# to the new object.
#
# INPUT: $a0: the first string object (self)
# Top of stack: the second string object (arg1)
#
# OUTPUT: $a0 the new string object
#
.globl String.concat
String.concat:
addiu $sp $sp -16
sw $ra 16($sp) # save return address
sw $a0 12($sp) # save self arg.
sw $0 8($sp) # init GC area
sw $0 4($sp) # init GC area
jal _MemMgr_Test # test GC area
lw $a0 12($sp)
lw $a0 str_size($a0)
jal _quick_copy # Call copy
sw $a0 8($sp) # save new size object
lw $t1 20($sp) # load arg object
lw $t1 str_size($t1) # get size object
lw $t1 int_slot($t1) # arg string size
blez $t1 _strcat_argempty # nothing to add
lw $t0 12($sp) # load self object
lw $t0 str_size($t0) # get size object
lw $t0 int_slot($t0) # self string size
addu $t0 $t0 $t1 # new size
sw $t0 int_slot($a0) # store new size
addiu $a0 $t0 str_field # size to allocate
addiu $a0 $a0 4 # include '\0', +3 to align
la $t2 0xfffffffc
and $a0 $a0 $t2 # align on word boundary
addiu $a0 $a0 1 # make size odd for GC <-|
sw $a0 4($sp) # save size in bytes |
addiu $a0 $a0 3 # include eyecatcher(4) -1
jal _MemMgr_QAlloc # check memory
lw $a0 12($sp) # copy self
jal _quick_copy # Call copy
lw $t0 8($sp) # get the Int object
sw $t0 str_size($a0) # store it in the str obj.
sub $t1 $gp $a0 # bytes allocated by _quick_copy
lw $t0 4($sp) # get size in bytes (no eyecatcher)
sub $t0 $t0 1 # Remove extra 1 (was for GC)
sub $t1 $t0 $t1 # more memory needed
addu $gp $gp $t1 # allocate rest
srl $t0 $t0 2 # convert to words
sw $t0 obj_size($a0) # save new object size
lw $t0 12($sp) # get original self object
lw $t0 str_size($t0) # get size object
lw $t0 int_slot($t0) # self string size
addiu $t1 $a0 str_field # points to start of string data
addu $t1 $t1 $t0 # points to end: '\0'
lw $t0 20($sp) # load arg object
addiu $t2 $t0 str_field # points to start of arg data
lw $t0 str_size($t0) # get arg size
lw $t0 int_slot($t0)
addu $t0 $t0 $t2 # find limit of copy
_strcat_copy:
lb $v0 0($t2) # load from source
sb $v0 0($t1) # save in destination
addiu $t2 $t2 1 # advance each index
addiu $t1 $t1 1
bne $t2 $t0 _strcat_copy # check limit
sb $0 0($t1) # add '\0'
lw $ra 16($sp) # restore return address
addiu $sp $sp 20 # pop argument
jr $ra # return
_strcat_argempty:
lw $a0 12($sp) # load original self
lw $ra 16($sp) # restore return address
addiu $sp $sp 20 # pop argument
jr $ra # return
#
#
# String.substr(i,l)
# Returns the sub string of self from i with length l
# Offset starts at 0.
#
# INPUT: $a0 the string
# length int object on top of stack (-4)
# index int object below length on stack (-8)
# OUTPUT: The substring object in $a0
#
.globl String.substr
String.substr:
addiu $sp $sp -12 # frame
sw $ra 4($sp) # save return
sw $a0 12($sp) # save self
sw $0 8($sp) # init GC area
jal _MemMgr_Test # test GC area
lw $a0 12($sp)
lw $v0 obj_size($a0)
la $a0 Int_protObj # ask if enough room to allocate
lw $a0 obj_size($a0) # a string object, an int object,
add $a0 $a0 $v0 # and the string data
addiu $a0 $a0 2 # include 2 eyecatchers
sll $a0 $a0 2
addi $a0 $a0 str_maxsize
jal _MemMgr_QAlloc
_ss_ok:
la $a0 Int_protObj
jal _quick_copy
jal Int_init
sw $a0 8($sp) # save new length obj
la $a0 String_protObj
jal _quick_copy
jal String_init # new obj ptr in $a0
move $a2 $a0 # use a2 to make copy
addiu $gp $gp -4 # backup alloc ptr
lw $a1 12($sp) # load orig
lw $t1 20($sp) # index obj
lw $t2 16($sp) # length obj
lw $t0 str_size($a1)
lw $v1 int_slot($t1) # index
lw $v0 int_slot($t0) # size of orig
bltz $v1 _ss_abort1 # index is smaller than 0
bgt $v1 $v0 _ss_abort2 # index > orig
lw $t3 int_slot($t2) # sub length
add $v1 $v1 $t3 # index+sublength
bgt $v1 $v0 _ss_abort3
bltz $t3 _ss_abort4
lw $t4 8($sp) # load new length obj
sw $t3 int_slot($t4) # save new size
sw $t4 str_size($a0) # store size in string
lw $v1 int_slot($t1) # index
addiu $a1 $a1 str_field # advance src to str
add $a1 $a1 $v1 # advance to indexed char
addiu $a2 $a2 str_field # advance dst to str
beqz $t3 _ss_end # empty length
_ss_loop:
lb $v0 0($a1)
addiu $a1 $a1 1 # inc src
sb $v0 0($a2)
addiu $a2 $a2 1 # inc dst
addiu $t3 $t3 -1 # dec ctr
bnez $t3 _ss_loop
_ss_end:
sb $zero 0($a2) # null terminate
move $gp $a2
addiu $gp $gp 4 # realign the heap ptr
la $t0 0xfffffffc
and $gp $gp $t0 # word align $gp
sub $t0 $gp $a0 # calc object size
srl $t0 $t0 2 # div by 4
sw $t0 obj_size($a0)
lw $ra 4($sp)
addiu $sp $sp 20 # pop arguments
jr $ra
_ss_abort1:
la $a0 _sabort_msg1
b _ss_abort
_ss_abort2:
la $a0 _sabort_msg2
b _ss_abort
_ss_abort3:
la $a0 _sabort_msg3
b _ss_abort
_ss_abort4:
la $a0 _sabort_msg4
_ss_abort:
li $v0 4
syscall
la $a0 _sabort_msg
li $v0 4
syscall
li $v0 10 # exit
syscall
#
# MemMgr Memory Manager
#
# The MemMgr functions give a consistent view of the garbage collectors.
# This allows multiple collectors to exist in one file and the easy
# selection of different collectors. It includes functions to initialize
# the collector and to reserve memory and query its status.
#
# The following assumptions are made:
#
# 1) The allocation of memory involves incrementing the $gp pointer.
# The $s7 pointer serves as a limit. The collector function is
# called before $s7 is exceeded by $gp.
#
# 2) The initialization functions all take the same arguments as
# defined in "_MemMgr_Init".
#
# 3) The garbage collector functions all take the arguments. "$a0"
# contains the end of the stack to check for pointers. "$a1"
# contains the size in bytes needed by the program and must be
# preserved across the function call.
#
#
# Initialize the Memory Manager
#
# Call the initialization routine for the garbage collector.
#
# INPUT:
# $a0: start of stack
# $a1: initial Register mask
# $a2: end of heap
# heap_start: start of the heap
#
# OUTPUT:
# $gp: lower bound of the work area
# $s7: upper bound of the work area
#
# Registers modified:
# $t0, initializer function
#
.globl _MemMgr_Init
_MemMgr_Init:
addiu $sp $sp -4
sw $ra 4($sp) # save return address
la $t0 _MemMgr_INITIALIZER # pointer to initialization
lw $t0 0($t0)
jalr $t0 # initialize
lw $ra 4($sp) # restore return address
addiu $sp $sp 4
jr $ra # return
#
# Memory Allocation
#
# Allocates the requested amount of memory and returns a pointer
# to the start of the block.
#
# INPUT:
# $a0: size of allocation in bytes
# $s7: limit pointer of the work area
# $gp: current allocation pointer
# heap_start: start of heap
#
# OUTPUT:
# $a0: pointer to new memory block
#
# Registers modified:
# $t0, $a1, collector function
#
.globl _MemMgr_Alloc
_MemMgr_Alloc:
add $gp $gp $a0 # attempt to allocate storage
blt $gp $s7 _MemMgr_Alloc_end # check allocation
sub $gp $gp $a0 # restore $gp
addiu $sp $sp -4
sw $ra 4($sp) # save return address
move $a1 $a0 # size
addiu $a0 $sp 4 # end of stack to collect
la $t0 _MemMgr_COLLECTOR # pointer to collector function
lw $t0 0($t0)
jalr $t0 # garbage collect
lw $ra 4($sp) # restore return address
addiu $sp $sp 4
move $a0 $a1 # put size into $a0
add $gp $gp $a0 # allocate storage
_MemMgr_Alloc_end:
sub $a0 $gp $a0
jr $ra # return
#
# Query Memory Allocation
#
# Verifies that the requested amount of memory can be allocated
# within the work area.
#
# INPUT:
# $a0: size of allocation in bytes
# $s7: limit pointer of the work area
# $gp: current allocation pointer
# heap_start: start of heap
#
# OUTPUT:
# $a0: size of allocation in bytes (unchanged)
#
# Registers modified:
# $t0, $a1, collector function
#
.globl _MemMgr_QAlloc
_MemMgr_QAlloc:
add $t0 $gp $a0 # attempt to allocate storage
blt $t0 $s7 _MemMgr_QAlloc_end # check allocation
addiu $sp $sp -4
sw $ra 4($sp) # save return address
move $a1 $a0 # size
addiu $a0 $sp 4 # end of stack to collect
la $t0 _MemMgr_COLLECTOR # pointer to collector function
lw $t0 0($t0)
jalr $t0 # garbage collect
lw $ra 4($sp) # restore return address
addiu $sp $sp 4
move $a0 $a1 # put size into $a0
_MemMgr_QAlloc_end:
jr $ra # return
#
# Test heap consistency
#
# Runs the garbage collector in the hope that this will help detect
# garbage collection bugs earlier.
#
# INPUT: (the usual GC stuff)
# $s7: limit pointer of the work area
# $gp: current allocation pointer
# heap_start: start of heap
#
# OUTPUT:
# none
#
# Registers modified:
# $t0, $a1, collector function
.globl _MemMgr_Test
_MemMgr_Test:
la $t0 _MemMgr_TEST # Check if testing enabled
lw $t0 0($t0)
beqz $t0 _MemMgr_Test_end
# Allocate 0 bytes
addiu $sp $sp -4 # Save return address
sw $ra 4($sp)
li $a1 0 # size = 0
addiu $a0 $sp 4 # end of stack to collect
la $t0 _MemMgr_COLLECTOR # pointer to collector function
lw $t0 0($t0)
jalr $t0 # garbage collect
lw $ra 4($sp) # restore return address
addiu $sp $sp 4
_MemMgr_Test_end:
jr $ra
#
# GenGC Generational Garbage Collector
#
# This is an implementation of a generational garbage collector
# as described in "Simple Generational Garbage Collection and Fast
# Allocation" by Andrew W. Appel [Princeton University, March 1988].
# This is a two generation scheme which uses an assignment table
# to handle root pointers located in the older generation objects.
#
# When the work area is filled, a minor garbage collection takes place
# which moves all live objects into the reserve area. These objects
# are then incorporated into the old area. New reserve and work areas
# are setup and allocation can continue in the work area. If a break-
# point is reached in the size of the old area just after a minor
# collection, a major collection then takes place. All live objects in
# the old area are then copied into the new area, expanding the heap if
# necessary. The X and new areas are then block copied back L1-L0
# bytes to form the next old area.
#
# The assignment table is implemented as a stack growing towards the
# allocation pointer ($gp) in the work area. If they cross, a minor
# collection is then carried out. This allows the garbage collector to
# to have to keep a fixed table of assignments. As a result, programs
# with many assignments will tend not to be bogged down with extra
# garbage collections.
#
# The unused area was implemented to help keep the garbage collector
# from continually expanding the heap. This buffer zone allows major
# garbage collections to happen earlier, reducing the risk of expansions
# due to too many live objects in the old area. The histories kept by
# the garbage collector in MAJOR0, MAJOR1, MINOR0, and MINOR1 also help
# to prevent unnecessary expansions of the heap. If many live objects
# were recently collected, the garbage collections will start to occur
# sooner.
#
# Note that during a minor collection, the work area is guaranteed to
# fit within the reserve area. However, during a major collection, the
# old area will not necessarily fit in the new area. If the latter occurs,
# "_GenGC_OfsCopy" will detect this and expand the heap.
#
# The heap is expanded on two different occasions:
#
# 1) After a major collection, the old area is set to be at most
# 1/(2^GenGC_OLDRATIO) of the usable heap (L0 to L3). Note that
# first L4 is checked to see if any of the unused memory between L3
# and L4 is enough to satisfy this requirement. If not, then the
# heap will be expanded. If it is, the appropriate amount will be
# transfered from the unused area to the work/reserve area.
#
# 2) During a major collection, if the live objects in the old area
# do not fit within the new area, the heap is expanded and $s7
# is updated to reflact this. This value later gets stored back
# into L4.
#
# During a normal allocation and minor collections, the heap has the
# following form:
#
# Header
# |
# | Older generation objects
# | |
# | | Minor garbage collection area
# | | |
# | | | Allocation area
# | | | |
# | | | | Assignment table
# | | | | |
# | | | | | Unused
# | | | | | |
# v v v v v v
# +----+--------------+-----------------+-------------+---+---------+
# |XXXX| Old Area | Reserve Area | Work Area |XXX| Unused |
# +----+--------------+-----------------+-------------+---+---------+
# ^ ^ ^ ^ ^ ^ ^ ^
# | | | | |--> <--| | |
# | L0 L1 L2 $gp $s7 L3 L4
# |
# heap_start
#
# $gp (allocation pointer): points to the next free word in the work
# area during normal allocation. During a minor garbage collection,
# it points to the next free work in the reserve area.
#
# $s7 (limit pointer): points to the limit that $gp can traverse. Between
# it and L3 sits the assignment table which grows towards $gp.
#
# During a Major collection, the heap has the following form:
#
# Header
# |
# | Older generation objects
# | |
# | | Objects surviving last minor garbage collection
# | | |
# | | | Major garbage collection area
# | | | |
# v v v v
# +----+------------------+----------+------------------------------+
# |XXXX| Old Area | X | New Area |
# +----+------------------+----------+------------------------------+
# ^ ^ ^ ^ ^ ^ ^
# | | | | | |--> |
# | L0 L1 | L2 $gp L4, $s7
# | |
# heap_start breakpoint
#
# $gp (allocation pointer): During a major collection, this points
# into the next free word in the new area.
#
# $s7 (limit pointer): During a major collection, the points to the
# limit of heap memory. $gp is not allowed to pass this value.
# If the objects in the live old area cannot fit in the new area,
# more memory is allocated and $s7 is adjusted accordingly.
#
# breakpoint: Point where a major collection will occur. It is
# calculated by the following formula:
#
# breakpoint = MIN(L3-MAX(MAJOR0,MAJOR1)-MAX(MINOR0,MINOR1),
# L3-(L3-L0)/2)
#
# where (variables stored in the header):
# MAJOR0 = total size of objects in the new area after last major
# collection.
# MAJOR1 = (MAJOR0+MAJOR1)/2
# MINOR0 = total size of objects in the reserve area after last
# minor collection.
# MINOR1 = (MINOR0+MINOR1)/2
#
# The following assumptions are made in the garbage collection
# process:
#
# 1) Pointers on the Stack:
# Every word on the stack that ends in 0 (i.e., is even) and is
# a valid address in the heap is assumed to point to an object
# in the heap. Even heap addresses on the stack that are actually
# something else (e.g., raw integers) will probably cause an
# garbage collection error.
#
# 2) Object Layout:
# Besides the Int, String, and Bool objects (which are handled
# separately), the garbage collector assumes that each attribute
# in an object is a pointer to another object. It, however,
# still does as much as possible to verify this before actually
# updating any fields.
#
# 3) Pointer tests:
# In order to be verified as an object, a pointer must undergo
# certain tests:
#
# a) The pointer must point within the correct storage area.
# b) The word before the pointer (obj_eyecatch) must be the
# word 0xFFFF FFFF
# c) The word at the pointer must not be 0xFFFF FFFF (i.e.
# -1 cannot be a class tag)
#
# These tests are performed whenever any data could be a pointer
# to keep any non-pointers from being updated accidentally. The
# functions "_GenGC_ChkCopy" and "_GenGC_OfsCopy" are responsible
# for these checks.
#
# 4) The size stored in the object does not include the word required
# to store the eyecatcher for the object in the heap. This allows
# the prototype objects to not require its own eyecatcher. Also,
# a size of 0 is invalid because it is used as a flag by the garbage
# collector to indicate a forwarding pointer in the "obj_disp" field.
#
# 5) Roots are contained in the following areas: the stack, registers
# specified in the REG mask, and the assignment table.
#
#
# Constants
#
#
# GenGC header offsets from "heap_start"
#
GenGC_HDRSIZE=44 # size of GenGC header
GenGC_HDRL0=0 # pointers to GenGC areas
GenGC_HDRL1=4
GenGC_HDRL2=8
GenGC_HDRL3=12
GenGC_HDRL4=16
GenGC_HDRMAJOR0=20 # history of major collections
GenGC_HDRMAJOR1=24
GenGC_HDRMINOR0=28 # history of minor collections
GenGC_HDRMINOR1=32
GenGC_HDRSTK=36 # start of stack
GenGC_HDRREG=40 # current REG mask
#
# Granularity of heap expansion
#
# The heap is always expanded in multiples of 2^k, where
# k is the granularity.
#
GenGC_HEAPEXPGRAN=14 # 2^14=16K
#
# Old to usable heap size ratio
#
# After a major collection, the ratio of size of old area to the usable
# size of the heap is at most 1/(2^k) where k is the value provided.
#
GenGC_OLDRATIO=2 # 1/(2^2)=.25=25%
#
# Mask to speficy which registers can be automatically updated
# when a garbage collection occurs. The Automatic Register Update
# (ARU) mask has a bit set for all possible registers the
# garbage collector is able to handle. The Register (REG) mask
# determines which register(s) are actually updated.
#
# BITS----------------------------
# 3 2 1 0
# 10987654321098765432109876543210
# --------------------------------
#
# 11000011011111110000000000000000 <- Auto Register Update (ARU) mask
# +--++--++--++--++--++--++--++--+ $s0-$s6, $t8-$t9, $s8, $ra
# C 3 7 F 0 0 0 0 ($16-$22, $24-$25, $30, $31)
#
GenGC_ARU_MASK=0xC37F0000
#
# Functions
#
#
# Initialization
#
# Sets up the header information block for the garbage collector.
# This block is located at the start of the heap ("heap_start")
# and includes information needed by the garbage collector. It
# also calculates the barrier for the reserve and work areas and
# sets the L2 pointer accordingly, rounding off in favor of the
# reserve area.
#
# INPUT:
# $a0: start of stack
# $a1: initial Register mask
# $a2: end of heap
# heap_start: start of the heap
#
# OUTPUT:
# $gp: lower bound of the work area
# $s7: upper bound of the work area
#
# Registers modified:
# $t0, $t1, $v0, $a0
#
.globl _GenGC_Init
_GenGC_Init:
la $t0 heap_start
addiu $t1 $t0 GenGC_HDRSIZE
sw $t1 GenGC_HDRL0($t0) # save start of old area
sw $t1 GenGC_HDRL1($t0) # save start of reserve area
sub $t1 $a2 $t1 # find reserve/work area barrier
srl $t1 $t1 1
la $v0 0xfffffffc
and $t1 $t1 $v0
blez $t1 _GenGC_Init_error # heap initially to small
sub $gp $a2 $t1
sw $gp GenGC_HDRL2($t0) # save start of work area
sw $a2 GenGC_HDRL3($t0) # save end of work area
move $s7 $a2 # set limit pointer
sw $0 GenGC_HDRMAJOR0($t0) # clear histories
sw $0 GenGC_HDRMAJOR1($t0)
sw $0 GenGC_HDRMINOR0($t0)
sw $0 GenGC_HDRMINOR1($t0)
sw $a0 GenGC_HDRSTK($t0) # save stack start
sw $a1 GenGC_HDRREG($t0) # save register mask
li $v0 9 # get heap end
move $a0 $zero
syscall # sbrk
sw $v0 GenGC_HDRL4($t0) # save heap limit
la $t0 _MemMgr_TEST # Check if testing enabled
lw $t0 0($t0)
beqz $t0 _MemMgr_Test_false
la $a0 _GenGC_Init_test_msg # tell user GC is in test mode
li $v0 4
syscall
j _GenGC_Init_end
_MemMgr_Test_false:
la $a0 _GenGC_Init_msg # tell user GC NOT in test mode
li $v0 4
syscall
_GenGC_Init_end:
jr $ra # return
_GenGC_Init_error:
la $a0 _GenGC_INITERROR # show error message
li $v0 4
syscall
li $v0 10 # exit
syscall
#
# Record Assignment
#
# Records an assignment in the assignment table. Note that because
# $s7 is always greater than $gp, an assignment can always be
# recorded.
#
# INPUT:
# $a1: pointer to the pointer being modified
# $s7: limit pointer of the work area
# $gp: current allocation pointer
# heap_start: start of heap
#
# Registers modified:
# $t0, $t1, $t2, $v0, $v1, $a1, $a2, $gp, $s7
#
# sm: $a0 is explicitly saved in the GC case so that in the normal
# case the caller need not save/restore $a0
#
# sm: Apparently _GenGC_Collect wants $a0 to be the last+1 word
# of the stack, rather than the last word; I've therefore changed
# addiu $a0 $sp 4
# to
# addiu $a0 $sp 0 (i.e. move $a0 $sp)
# Just in case this isn't exactly right, I've also put 0 into that
# last spot so it will definitely be safely ignored.
#
.globl _GenGC_Assign
_GenGC_Assign:
addiu $s7 $s7 -4
sw $a1 0($s7) # save pointer to assignment
bgt $s7 $gp _GenGC_Assign_done
addiu $sp $sp -8
sw $ra 8($sp) # save return address
sw $a0 4($sp) # sm: save $a0
move $a1 $0 # size
addiu $a0 $sp 0 # end of stack to collect
sw $0 0($sp) # play it safe with off-by-1
jal _GenGC_Collect
lw $ra 8($sp) # restore return address
lw $a0 4($sp) # restore $a0
addiu $sp $sp 8
_GenGC_Assign_done:
jr $ra # return
.globl _gc_check
_gc_check:
beqz $a1, _gc_ok # void is ok
lw $a2 obj_eyecatch($a1) # and check if it is valid
addiu $a2 $a2 1
bnez $a2 _gc_abort
_gc_ok:
jr $ra
_gc_abort:
la $a0 _gc_abort_msg
li $v0 4
syscall # print gc message
li $v0 10
syscall # exit
#
# Generational Garbage Collection
#
# This function implements the generational garbage collection.
# It first calls the minor collector, "_GenGC_MinorC", and then
# updates its history in the header. The breakpoint is then
# calculated. If the breakpoint is reached or there is still not
# enough room to allocate the requested size, a major garbage
# collection then takes place by calling "_GenGC_MajorC". After
# the major collection, the size of the old area is analyzed. If
# it is greater than 1/(2^GenGC_OLDRATIO) of the total usable heap
# size (L0 to L3), the heap is expanded. Also, if there is still not
# enough room to allocate the requested size, the heap is expanded
# further to make sure that the specified amount of memory can be
# allocated. If there is enough room in the unused area (L3 to L4),
# this memory is used and the heap is not expanded. The $s7 and $gp
# pointers are then set as well as the L2 pointer. If a major collection
# is not done, the X area is incorporated into the old area
# (i.e. the L2 pointer is moved into L1) and $s7, $gp, and L2 are
# then set.
#
# INPUT:
# $a0: end of stack
# $a1: size will need to allocate in bytes
# $s7: limit pointer of thw work area
# $gp: current allocation pointer
# heap_start: start of heap
#
# OUTPUT:
# $a1: size will need to allocate in bytes (unchanged)
#
# Registers modified:
# $t0, $t1, $t2, $t3, $t4, $v0, $v1, $a0, $a2, $gp, $s7
#
.globl _GenGC_Collect
_GenGC_Collect:
addiu $sp $sp -12
sw $ra 12($sp) # save return address
sw $a0 8($sp) # save stack end
sw $a1 4($sp) # save size
la $a0 _GenGC_COLLECT # print collection message
li $v0 4
syscall
lw $a0 8($sp) # restore stack end
jal _GenGC_MinorC # minor collection
la $a1 heap_start
lw $t1 GenGC_HDRMINOR1($a1)
addu $t1 $t1 $a0
srl $t1 $t1 1
sw $t1 GenGC_HDRMINOR1($a1) # update histories
sw $a0 GenGC_HDRMINOR0($a1)
move $t0 $t1 # set $t0 to max of minor
bgt $t1 $a0 _GenGC_Collect_maxmaj
move $t0 $a0
_GenGC_Collect_maxmaj:
lw $t1 GenGC_HDRMAJOR0($a1) # set $t1 to max of major
lw $t2 GenGC_HDRMAJOR1($a1)
bgt $t1 $t2 _GenGC_Collect_maxdef
move $t1 $t2
_GenGC_Collect_maxdef:
lw $t2 GenGC_HDRL3($a1)
sub $t0 $t2 $t0 # set $t0 to L3-$t0-$t1
sub $t0 $t0 $t1
lw $t1 GenGC_HDRL0($a1) # set $t1 to L3-(L3-L0)/2
sub $t1 $t2 $t1
srl $t1 $t1 1
sub $t1 $t2 $t1
blt $t0 $t1 _GenGC_Collect_breakpt # set $t0 to minimum of above
move $t0 $t1
_GenGC_Collect_breakpt:
lw $t1 GenGC_HDRL1($a1) # get end of old area
bge $t1 $t0 _GenGC_Collect_major
lw $t0 GenGC_HDRL2($a1)
lw $t1 GenGC_HDRL3($a1)
lw $t2 4($sp) # load requested size into $t2
sub $t0 $t1 $t0 # find reserve/work area barrier
srl $t0 $t0 1
la $t3 0xfffffffc
and $t0 $t0 $t3
sub $t0 $t1 $t0 # reserve/work barrier
addu $t2 $t0 $t2 # test allocation
bge $t2 $t1 _GenGC_Collect_major # check if work area too small
_GenGC_Collect_nomajor:
lw $t1 GenGC_HDRL2($a1)
sw $t1 GenGC_HDRL1($a1) # expand old area
sw $t0 GenGC_HDRL2($a1) # set new reserve/work barrier
move $gp $t0 # set $gp
lw $s7 GenGC_HDRL3($a1) # load limit into $s7
b _GenGC_Collect_done
_GenGC_Collect_major:
la $a0 _GenGC_Major # print collection message
li $v0 4
syscall
lw $a0 8($sp) # restore stack end
jal _GenGC_MajorC # major collection
la $a1 heap_start
lw $t1 GenGC_HDRMAJOR1($a1)
addu $t1 $t1 $a0
srl $t1 $t1 1
sw $t1 GenGC_HDRMAJOR1($a1) # update histories
sw $a0 GenGC_HDRMAJOR0($a1)
lw $t1 GenGC_HDRL3($a1) # find ratio of the old area
lw $t0 GenGC_HDRL0($a1)
sub $t1 $t1 $t0
srl $t1 $t1 GenGC_OLDRATIO
addu $t1 $t0 $t1
lw $t0 GenGC_HDRL1($a1)
sub $t0 $t0 $t1
sll $t0 $t0 GenGC_OLDRATIO # amount to expand in $t0
lw $t1 GenGC_HDRL3($a1) # load L3
lw $t2 GenGC_HDRL1($a1) # load L1
sub $t2 $t1 $t2
srl $t2 $t2 1
la $t3 0xfffffffc
and $t2 $t2 $t3
sub $t1 $t1 $t2 # reserve/work barrier
lw $t2 4($sp) # restore size
addu $t1 $t1 $t2
lw $t2 GenGC_HDRL3($a1) # load L3
sub $t1 $t1 $t2 # test allocation
addiu $t1 $t1 4 # adjust for round off errors
sll $t1 $t1 1 # need to allocate $t1 memory
blt $t1 $t0 _GenGC_Collect_enough # put max of $t0, $t1 in $t0
move $t0 $t1
_GenGC_Collect_enough:
blez $t0 _GenGC_Collect_setL2 # no need to expand
addiu $t1 $0 1 # put 1 in $t1
sll $t1 $t1 GenGC_HEAPEXPGRAN # get granularity of expansion
addiu $t1 $t1 -1 # align to granularity
addu $t0 $t0 $t1
nor $t1 $t1 $t1
and $t0 $t0 $t1 # total memory needed
lw $t1 GenGC_HDRL3($a1) # load L3
lw $t2 GenGC_HDRL4($a1) # load L4
sub $t1 $t2 $t1
sub $t2 $t0 $t1 # actual amount to allocate
bgtz $t2 _GenGC_Collect_getmem # check if really need to allocate
_GenGC_Collect_xfermem:
lw $s7 GenGC_HDRL3($a1) # load L3
addu $s7 $s7 $t0 # expand by $t0, set $s7
sw $s7 GenGC_HDRL3($a1) # save L3
b _GenGC_Collect_findL2
_GenGC_Collect_getmem:
li $v0 9 # sbrk
move $a0 $t2 # set the size to expand the heap
syscall
li $v0 9
move $a0 $zero
syscall # get new end of heap in $v0
sw $v0 GenGC_HDRL4($a1) # save L4
sw $v0 GenGC_HDRL3($a1) # save L3
move $s7 $v0 # set $s7
b _GenGC_Collect_findL2
_GenGC_Collect_setL2:
lw $s7 GenGC_HDRL3($a1) # load L3
_GenGC_Collect_findL2:
lw $t1 GenGC_HDRL1($a1) # load L1
sub $t1 $s7 $t1
srl $t1 $t1 1
la $t0 0xfffffffc
and $t1 $t1 $t0
sub $gp $s7 $t1 # reserve/work barrier
sw $gp GenGC_HDRL2($a1) # save L2
_GenGC_Collect_done:
# Clear new generation to catch missing pointers
move $t0 $gp
_GenGC_Clear_loop:
sw $zero 0($t0)
addiu $t0 $t0 4
blt $t0 $s7 _GenGC_Clear_loop
lw $a1 4($sp) # restore size
lw $ra 12($sp) # restore return address
addiu $sp $sp 12
jr $ra # return
#
# Check and Copy an Object
#
# Checks that the input pointer points to an object is a heap
# object. If so, it then checks for a forwarding pointer by
# checking for an object size of 0. If found, the forwarding
# pointer is returned. If not found, the object is copied to $gp
# and a pointer to it is returned. The following tests are done to
# determine if the object is a heap object:
#
# 1) The pointer is within the specified limits
# 2) The pointer is even
# 3) The word before the pointer is the eye catcher 0xFFFF FFFF
# 4) The word at the pointer is a valid tag (i.e. not equal to
# 0xFFFF FFFF)
#
# INPUT:
# $a0: pointer to check and copy
# $a1: lower bound object should be within.
# $a2: upper bound object should be within.
# $gp: current allocation pointer
#
# OUTPUT:
# $a0: if input points to a heap object then it is set to the
# new location of object. If not, it is unchanged.
# $a1: lower bound object should be within. (unchanged)
# $a2: upper bound object should be within. (unchanged)
#
# Registers modified:
# $t0, $t1, $t2, $v0, $a0, $gp
#
.globl _GenGC_ChkCopy
_GenGC_ChkCopy:
blt $a0 $a1 _GenGC_ChkCopy_done # check bounds
bge $a0 $a2 _GenGC_ChkCopy_done
andi $t2 $a0 1 # check if odd
bnez $t2 _GenGC_ChkCopy_done
addiu $t2 $0 -1
lw $t1 obj_eyecatch($a0) # check eyecatcher
bne $t2 $t1 _gc_abort
lw $t1 obj_tag($a0) # check object tag
beq $t2 $t1 _GenGC_ChkCopy_done
lw $t1 obj_size($a0) # get size of object
beqz $t1 _GenGC_ChkCopy_forward # if size = 0, get forwarding pointer
move $t0 $a0 # save pointer to old object in $t0
addiu $gp $gp 4 # allocate memory for eyecatcher
move $a0 $gp # get address of new object
sw $t2 obj_eyecatch($a0) # save eye catcher
sll $t1 $t1 2 # convert words to bytes
addu $t1 $t0 $t1 # set $t1 to limit of copy
move $t2 $t0 # set $t2 to old object
_GenGC_ChkCopy_loop:
lw $v0 0($t0) # copy
sw $v0 0($gp)
addiu $t0 $t0 4 # update each index
addiu $gp $gp 4
bne $t0 $t1 _GenGC_ChkCopy_loop # check for limit of copy
sw $0 obj_size($t2) # set size to 0
sw $a0 obj_disp($t2) # save forwarding pointer
_GenGC_ChkCopy_done:
jr $ra # return
_GenGC_ChkCopy_forward:
lw $a0 obj_disp($a0) # get forwarding pointer
jr $ra # return
#
# Minor Garbage Collection
#
# This garbage collector is run when ever the space in the work
# area is used up by objects and the assignment table. The live
# objects are found and copied to the reserve area. The L2 pointer
# is then set to the end of the live objects. The collector consists
# of six phases:
#
# 1) Set $gp into the reserve area and set the inputs for ChkCopy
#
# 2) Scan the stack for root pointers into the heap. The beginning
# of the stack is in the header and the end is an input to this
# function. Look for the appropriate stack flags and act
# accordingly. Use "_GenGC_ChkCopy" to validate the pointer and
# get the new pointer, and then update the stack entry.
#
# 3) Check the registers specified in the Register (REG) mask to
# automatically update. This mask is stored in the header. If
# bit #n in the mask is set, register #n will be passed to
# "_GenGC_ChkCopy" and updated with its result. "_GenGC_SetRegMask"
# can be used to update this mask.
#
# 4) The assignemnt table is now checked. $s7 is moved from its
# current position until it hits the L3 pointer. Each entry is a
# pointer to the pointer that must be checked. Again,
# "_GenGC_ChkCopy" is used and the pointer updated.
#
# 5) At this point, all root objects are in the reserve area. This
# area is now traversed object by object (from L1 to $gp). It
# results in a breadth first search of the live objects collected.
# All attributes of objects are treated as pointers except the
# "Int", "Bool", and "String" objects. The first two are skipped
# completely, and the first attribute of the string object is
# analyzed (should be a pointer to an "Int" object).
#
# 6) At this point, L2 is set to the end of the live objects in the
# reserve area. This is in preparation for a major collection.
# The size of all the live objects collected is then computed and
# returned.
#
# INPUT:
# $a0: end of stack
# $s7: limit pointer of this area of storage
# $gp: current allocation pointer
# heap_start: start of heap
#
# OUTPUT:
# $a0: size of all live objects collected
#
# Registers modified:
# $t0, $t1, $t2, $t3, $t4, $v0, $v1, $a0, $a1, $a2, $gp, $s7
#
.globl _GenGC_MinorC
_GenGC_MinorC:
addiu $sp $sp -20
sw $ra 20($sp) # save return address
la $t0 heap_start
lw $a1 GenGC_HDRL2($t0) # set lower bound to work area
move $a2 $s7 # set upper bound for ChkCopy
lw $gp GenGC_HDRL1($t0) # set $gp into reserve area
sw $a0 16($sp) # save stack end
lw $t0 GenGC_HDRSTK($t0) # set $t0 to stack start
move $t1 $a0 # set $t1 to stack end
ble $t0 $t1 _GenGC_MinorC_stackend # check for empty stack
_GenGC_MinorC_stackloop: # $t1 stack end, $t0 index
addiu $t0 $t0 -4 # update index
sw $t0 12($sp) # save stack index
lw $a0 4($t0) # get stack item
jal _GenGC_ChkCopy # check and copy
lw $t0 12($sp) # load stack index
sw $a0 4($t0)
lw $t1 16($sp) # restore stack end
bgt $t0 $t1 _GenGC_MinorC_stackloop # loop
_GenGC_MinorC_stackend:
la $t0 heap_start
lw $t0 GenGC_HDRREG($t0) # get Register mask
sw $t0 16($sp) # save Register mask
_GenGC_MinorC_reg16:
srl $t0 $t0 16 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg17 # check if set
move $a0 $16 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $16 $a0 # update register
_GenGC_MinorC_reg17:
lw $t0 16($sp) # restore mask
srl $t0 $t0 17 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg18 # check if set
move $a0 $17 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $17 $a0 # update register
_GenGC_MinorC_reg18:
lw $t0 16($sp) # restore mask
srl $t0 $t0 18 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg19 # check if set
move $a0 $18 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $18 $a0 # update register
_GenGC_MinorC_reg19:
lw $t0 16($sp) # restore mask
srl $t0 $t0 19 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg20 # check if set
move $a0 $19 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $19 $a0 # update register
_GenGC_MinorC_reg20:
lw $t0 16($sp) # restore mask
srl $t0 $t0 20 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg21 # check if set
move $a0 $20 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $20 $a0 # update register
_GenGC_MinorC_reg21:
lw $t0 16($sp) # restore mask
srl $t0 $t0 21 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg22 # check if set
move $a0 $21 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $21 $a0 # update register
_GenGC_MinorC_reg22:
lw $t0 16($sp) # restore mask
srl $t0 $t0 22 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg24 # check if set
move $a0 $22 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $22 $a0 # update register
_GenGC_MinorC_reg24:
lw $t0 16($sp) # restore mask
srl $t0 $t0 24 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg25 # check if set
move $a0 $24 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $24 $a0 # update register
_GenGC_MinorC_reg25:
lw $t0 16($sp) # restore mask
srl $t0 $t0 25 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg30 # check if set
move $a0 $25 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $25 $a0 # update register
_GenGC_MinorC_reg30:
lw $t0 16($sp) # restore mask
srl $t0 $t0 30 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_reg31 # check if set
move $a0 $30 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $30 $a0 # update register
_GenGC_MinorC_reg31:
lw $t0 16($sp) # restore mask
srl $t0 $t0 31 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MinorC_regend # check if set
move $a0 $31 # set test pointer
jal _GenGC_ChkCopy # check and copy
move $31 $a0 # update register
_GenGC_MinorC_regend:
la $t0 heap_start
lw $t3 GenGC_HDRL0($t0) # lower limit of old area
lw $t4 GenGC_HDRL1($t0) # upper limit of old area
lw $t0 GenGC_HDRL3($t0) # get L3
sw $t0 16($sp) # save index limit
bge $s7 $t0 _GenGC_MinorC_assnend # check for no assignments
_GenGC_MinorC_assnloop: # $s7 index, $t0 limit
lw $a0 0($s7) # get table entry
blt $a0 $t3 _GenGC_MinorC_assnnext # must point into old area
bge $a0 $t4 _GenGC_MinorC_assnnext
lw $a0 0($a0) # get pointer to check
jal _GenGC_ChkCopy # check and copy
lw $t0 0($s7)
sw $a0 0($t0) # update pointer
lw $t0 16($sp) # restore index limit
_GenGC_MinorC_assnnext:
addiu $s7 $s7 4 # update index
blt $s7 $t0 _GenGC_MinorC_assnloop # loop
_GenGC_MinorC_assnend:
la $t0 heap_start
lw $t0 GenGC_HDRL1($t0) # start of reserve area
bge $t0 $gp _GenGC_MinorC_heapend # check for no objects
_GenGC_MinorC_heaploop: # $t0: index, $gp: limit
addiu $t0 $t0 4 # skip over eyecatcher
addiu $t1 $0 -1 # check for eyecatcher
lw $t2 obj_eyecatch($t0)
bne $t1 $t2 _GenGC_MinorC_error # eyecatcher not found
lw $a0 obj_size($t0) # get object size
sll $a0 $a0 2 # words to bytes
lw $t1 obj_tag($t0) # get the object's tag
lw $t2 _int_tag # test for int object
beq $t1 $t2 _GenGC_MinorC_int
lw $t2 _bool_tag # test for bool object
beq $t1 $t2 _GenGC_MinorC_bool
lw $t2 _string_tag # test for string object
beq $t1 $t2 _GenGC_MinorC_string
_GenGC_MinorC_other:
addi $t1 $t0 obj_attr # start at first attribute
add $t2 $t0 $a0 # limit of attributes
bge $t1 $t2 _GenGC_MinorC_nextobj # check for no attributes
sw $t0 16($sp) # save pointer to object
sw $a0 12($sp) # save object size
sw $t2 4($sp) # save limit
_GenGC_MinorC_objloop: # $t1: index, $t2: limit
sw $t1 8($sp) # save index
lw $a0 0($t1) # set pointer to check
jal _GenGC_ChkCopy # check and copy
lw $t1 8($sp) # restore index
sw $a0 0($t1) # update object pointer
lw $t2 4($sp) # restore limit
addiu $t1 $t1 4
blt $t1 $t2 _GenGC_MinorC_objloop # loop
_GenGC_MinorC_objend:
lw $t0 16($sp) # restore pointer to object
lw $a0 12($sp) # restore object size
b _GenGC_MinorC_nextobj # next object
_GenGC_MinorC_string:
sw $t0 16($sp) # save pointer to object
sw $a0 12($sp) # save object size
lw $a0 str_size($t0) # set test pointer
jal _GenGC_ChkCopy # check and copy
lw $t0 16($sp) # restore pointer to object
sw $a0 str_size($t0) # update size pointer
lw $a0 12($sp) # restore object size
_GenGC_MinorC_int:
_GenGC_MinorC_bool:
_GenGC_MinorC_nextobj:
add $t0 $t0 $a0 # find next object
blt $t0 $gp _GenGC_MinorC_heaploop # loop
_GenGC_MinorC_heapend:
la $t0 heap_start
sw $gp GenGC_HDRL2($t0) # set L2 to $gp
lw $a0 GenGC_HDRL1($t0)
sub $a0 $gp $a0 # find size after collection
lw $ra 20($sp) # restore return address
addiu $sp $sp 20
jr $ra # return
_GenGC_MinorC_error:
la $a0 _GenGC_MINORERROR # show error message
li $v0 4
syscall
li $v0 10 # exit
syscall
#
# Check and Copy an Object with an Offset
#
# Checks that the input pointer points to an object is a heap object.
# If so, the pointer is checked to be in one of two areas. If the
# pointer is in the X area, L0-L1 is added to the pointer, and the
# new pointer is returned. If the pointer points within the old area,
# it then checks for a forwarding pointer by checking for an object
# size of 0. If found, the forwarding pointer is returned. If not
# found, the heap is then analyzed to make sure the object can be
# copied. It then expands the heap if necessary (updating only $s7),
# and the copies the object to the $gp pointer. It takes the new
# pointer, adds L0-L1 to it, then saves this modified new pointer in
# the forwarding (obj_disp) field and sets the flag (obj_size to 0).
# Finally, it returns this pointer. Note that this pointer does not
# actually point to the object at this time. This entire area will
# later be block copied. After that, this pointer will be valid.
# The same tests are done here as in "_GenGC_ChkCopy" to verify that
# this is a heap object.
#
# INPUT:
# $a0: pointer to check and copy with an offset
# $a1: L0 pointer
# $a2: L1 pointer
# $v1: L2 pointer
# $gp: current allocation pointer
# $s7: L4 pointer
#
# OUTPUT:
# $a0: if input points to a heap object then it is set to the
# new location of object. If not, it is unchanged.
# $a1: L0 pointer (unchanged)
# $a2: L1 pointer (unchanged)
# $v1: L2 pointer (unchanged)
#
# Registers modified:
# $t0, $t1, $t2, $v0, $a0, $gp, $s7
#
.globl _GenGC_OfsCopy
_GenGC_OfsCopy:
blt $a0 $a1 _GenGC_OfsCopy_done # check lower bound
bge $a0 $v1 _GenGC_OfsCopy_done # check upper bound
andi $t2 $a0 1 # check if odd
bnez $t2 _GenGC_OfsCopy_done
addiu $t2 $0 -1
lw $t1 obj_eyecatch($a0) # check eyecatcher
bne $t2 $t1 _gc_abort
lw $t1 obj_tag($a0) # check object tag
beq $t2 $t1 _GenGC_OfsCopy_done
blt $a0 $a2 _GenGC_OfsCopy_old # check if old, X object
sub $v0 $a1 $a2 # compute offset
add $a0 $a0 $v0 # apply pointer offset
jr $ra # return
_GenGC_OfsCopy_old:
lw $t1 obj_size($a0) # get size of object
sll $t1 $t1 2 # convert words to bytes
beqz $t1 _GenGC_OfsCopy_forward # if size = 0, get forwarding pointer
move $t0 $a0 # save pointer to old object in $t0
addu $v0 $gp $t1 # test allocation
addiu $v0 $v0 4
blt $v0 $s7 _GenGC_OfsCopy_memok # check if enoguh room for object
sub $a0 $v0 $s7 # amount to expand minus 1
addiu $v0 $0 1
sll $v0 $v0 GenGC_HEAPEXPGRAN
add $a0 $a0 $v0
addiu $v0 $v0 -1
nor $v0 $v0 $v0 # get grain mask
and $a0 $a0 $v0 # align to grain size
li $v0 9
syscall # expand heap
li $v0 9
move $a0 $0
syscall # get end of heap in $v0
move $s7 $v0 # save heap end in $s7
move $a0 $t0 # restore pointer to old object in $a0
_GenGC_OfsCopy_memok:
addiu $gp $gp 4 # allocate memory for eyecatcher
move $a0 $gp # get address of new object
sw $t2 obj_eyecatch($a0) # save eye catcher
addu $t1 $t0 $t1 # set $t1 to limit of copy
move $t2 $t0 # set $t2 to old object
_GenGC_OfsCopy_loop:
lw $v0 0($t0) # copy
sw $v0 0($gp)
addiu $t0 $t0 4 # update each index
addiu $gp $gp 4
bne $t0 $t1 _GenGC_OfsCopy_loop # check for limit of copy
sw $0 obj_size($t2) # set size to 0
sub $v0 $a1 $a2 # compute offset
add $a0 $a0 $v0 # apply pointer offset
sw $a0 obj_disp($t2) # save forwarding pointer
_GenGC_OfsCopy_done:
jr $ra # return
_GenGC_OfsCopy_forward:
lw $a0 obj_disp($a0) # get forwarding pointer
jr $ra # return
#
# Major Garbage Collection
#
# This collection occurs when ever the old area grows beyond a specified
# point. The minor collector sets up the Old, X, and New areas for
# this collector. It then collects all the live objects in the old
# area (L0 to L1) into the new area (L2 to L3). This collection consists
# of five phases:
#
# 1) Set $gp into the new area (L2), and $s7 to L4. Also set the
# inputs for "_GenGC_OfsCopy".
#
# 2) Traverse the stack (see the minor collector) using "_GenGC_OfsCopy".
#
# 3) Check the registers (see the minor collector) using "_GenGC_OfsCopy".
#
# 4) Traverse the heap from L1 to $gp using "_GenGC_OfsCopy". Note
# that this includes the X area. (see the minor collector)
#
# 5) Block copy the region L1 to $gp back L1-L0 bytes to create the
# next old area. Save the end in L1. Calculate the size of the
# live objects collected from the old area and return this value.
#
# Note that the pointers returned by "_GenGC_OfsCopy" are not valid
# until the block copy is done.
#
# INPUT:
# $a0: end of stack
# heap_start: start of heap
#
# OUTPUT:
# $a0: size of all live objects collected
#
# Registers modified:
# $t0, $t1, $t2, $v0, $v1, $a0, $a1, $a2, $gp, $s7
#
.globl _GenGC_MajorC
_GenGC_MajorC:
addiu $sp $sp -20
sw $ra 20($sp) # save return address
la $t0 heap_start
lw $s7 GenGC_HDRL4($t0) # limit pointer for collection
lw $gp GenGC_HDRL2($t0) # allocation pointer for collection
lw $a1 GenGC_HDRL0($t0) # set inputs for OfsCopy
lw $a2 GenGC_HDRL1($t0)
lw $v1 GenGC_HDRL2($t0)
sw $a0 16($sp) # save stack end
lw $t0 GenGC_HDRSTK($t0) # set $t0 to stack start
move $t1 $a0 # set $t1 to stack end
ble $t0 $t1 _GenGC_MajorC_stackend # check for empty stack
_GenGC_MajorC_stackloop: # $t1 stack end, $t0 index
addiu $t0 $t0 -4 # update index
sw $t0 12($sp) # save stack index
lw $a0 4($t0) # get stack item
jal _GenGC_OfsCopy # check and copy
lw $t0 12($sp) # load stack index
sw $a0 4($t0)
lw $t1 16($sp) # restore stack end
bgt $t0 $t1 _GenGC_MajorC_stackloop # loop
_GenGC_MajorC_stackend:
la $t0 heap_start
lw $t0 GenGC_HDRREG($t0) # get Register mask
sw $t0 16($sp) # save Register mask
_GenGC_MajorC_reg16:
srl $t0 $t0 16 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg17 # check if set
move $a0 $16 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $16 $a0 # update register
_GenGC_MajorC_reg17:
lw $t0 16($sp) # restore mask
srl $t0 $t0 17 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg18 # check if set
move $a0 $17 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $17 $a0 # update register
_GenGC_MajorC_reg18:
lw $t0 16($sp) # restore mask
srl $t0 $t0 18 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg19 # check if set
move $a0 $18 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $18 $a0 # update register
_GenGC_MajorC_reg19:
lw $t0 16($sp) # restore mask
srl $t0 $t0 19 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg20 # check if set
move $a0 $19 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $19 $a0 # update register
_GenGC_MajorC_reg20:
lw $t0 16($sp) # restore mask
srl $t0 $t0 20 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg21 # check if set
move $a0 $20 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $20 $a0 # update register
_GenGC_MajorC_reg21:
lw $t0 16($sp) # restore mask
srl $t0 $t0 21 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg22 # check if set
move $a0 $21 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $21 $a0 # update register
_GenGC_MajorC_reg22:
lw $t0 16($sp) # restore mask
srl $t0 $t0 22 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg24 # check if set
move $a0 $22 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $22 $a0 # update register
_GenGC_MajorC_reg24:
lw $t0 16($sp) # restore mask
srl $t0 $t0 24 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg25 # check if set
move $a0 $24 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $24 $a0 # update register
_GenGC_MajorC_reg25:
lw $t0 16($sp) # restore mask
srl $t0 $t0 25 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg30 # check if set
move $a0 $25 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $25 $a0 # update register
_GenGC_MajorC_reg30:
lw $t0 16($sp) # restore mask
srl $t0 $t0 30 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_reg31 # check if set
move $a0 $30 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $30 $a0 # update register
_GenGC_MajorC_reg31:
lw $t0 16($sp) # restore mask
srl $t0 $t0 31 # shift to proper bit
andi $t1 $t0 1
beq $t1 $0 _GenGC_MajorC_regend # check if set
move $a0 $31 # set test pointer
jal _GenGC_OfsCopy # check and copy
move $31 $a0 # update register
_GenGC_MajorC_regend:
la $t0 heap_start
lw $t0 GenGC_HDRL1($t0) # start of X area
bge $t0 $gp _GenGC_MajorC_heapend # check for no objects
_GenGC_MajorC_heaploop: # $t0: index, $gp: limit
addiu $t0 $t0 4 # skip over eyecatcher
addiu $t1 $0 -1 # check for eyecatcher
lw $t2 obj_eyecatch($t0)
bne $t1 $t2 _GenGC_MajorC_error # eyecatcher not found
lw $a0 obj_size($t0) # get object size
sll $a0 $a0 2 # words to bytes
lw $t1 obj_tag($t0) # get the object's tag
lw $t2 _int_tag # test for int object
beq $t1 $t2 _GenGC_MajorC_int
lw $t2 _bool_tag # test for bool object
beq $t1 $t2 _GenGC_MajorC_bool
lw $t2 _string_tag # test for string object
beq $t1 $t2 _GenGC_MajorC_string
_GenGC_MajorC_other:
addi $t1 $t0 obj_attr # start at first attribute
add $t2 $t0 $a0 # limit of attributes
bge $t1 $t2 _GenGC_MajorC_nextobj # check for no attributes
sw $t0 16($sp) # save pointer to object
sw $a0 12($sp) # save object size
sw $t2 4($sp) # save limit
_GenGC_MajorC_objloop: # $t1: index, $t2: limit
sw $t1 8($sp) # save index
lw $a0 0($t1) # set pointer to check
jal _GenGC_OfsCopy # check and copy
lw $t1 8($sp) # restore index
sw $a0 0($t1) # update object pointer
lw $t2 4($sp) # restore limit
addiu $t1 $t1 4
blt $t1 $t2 _GenGC_MajorC_objloop # loop
_GenGC_MajorC_objend:
lw $t0 16($sp) # restore pointer to object
lw $a0 12($sp) # restore object size
b _GenGC_MajorC_nextobj # next object
_GenGC_MajorC_string:
sw $t0 16($sp) # save pointer to object
sw $a0 12($sp) # save object size
lw $a0 str_size($t0) # set test pointer
jal _GenGC_OfsCopy # check and copy
lw $t0 16($sp) # restore pointer to object
sw $a0 str_size($t0) # update size pointer
lw $a0 12($sp) # restore object size
_GenGC_MajorC_int:
_GenGC_MajorC_bool:
_GenGC_MajorC_nextobj:
add $t0 $t0 $a0 # find next object
blt $t0 $gp _GenGC_MajorC_heaploop # loop
_GenGC_MajorC_heapend:
la $t0 heap_start
lw $a0 GenGC_HDRL2($t0) # get end of collection
sub $a0 $gp $a0 # get length after collection
lw $t1 GenGC_HDRL0($t0) # get L0
lw $t2 GenGC_HDRL1($t0) # get L1
bge $t2 $gp _GenGC_MajorC_bcpyend # test for empty copy
_GenGC_MajorC_bcpyloop: # $t2 index, $gp limit, $t1 dest
lw $v0 0($t2) # copy
sw $v0 0($t1)
addiu $t2 $t2 4 # update each index
addiu $t1 $t1 4
bne $t2 $gp _GenGC_MajorC_bcpyloop # loop
_GenGC_MajorC_bcpyend:
sw $s7 GenGC_HDRL4($t0) # save end of heap
lw $t1 GenGC_HDRL0($t0) # get L0
lw $t2 GenGC_HDRL1($t0) # get L1
sub $t1 $t2 $t1 # find offset of block copy
sub $gp $gp $t1 # find end of old area
sw $gp GenGC_HDRL1($t0) # save end of old area
lw $ra 20($sp) # restore return address
addiu $sp $sp 20
jr $ra # return
_GenGC_MajorC_error:
la $a0 _GenGC_MAJORERROR # show error message
li $v0 4
syscall
li $v0 10 # exit
syscall
#
# Set the Register (REG) mask
#
# If bit #n is set in the Register mask, register #n will be
# automatically updated by the garbage collector. Note that
# this mask is masked (ANDed) with the ARU mask. Only those
# registers in the ARU mask can be updated automatically.
#
# INPUT:
# $a0: new Register (REG) mask
# heap_start: start of the heap
#
# Registers modified:
# $t0
#
.globl _GenGC_SetRegMask
_GenGC_SetRegMask:
li $t0 GenGC_ARU_MASK # apply Automatic Register Mask (ARU)
and $a0 $a0 $t0
la $t0 heap_start # set $t0 to the start of the heap
sw $a0 GenGC_HDRREG($t0) # save the Register mask
jr $ra # return
#
# Query the Register (REG) mask
#
# INPUT:
# heap_start: start of the heap
#
# OUTPUT:
# $a0: current Register (REG) mask
#
# Registers modified:
# none
#
.globl _GenGC_QRegMask
_GenGC_QRegMask:
la $a0 heap_start # set $a0 to the start of the heap
lw $a0 GenGC_HDRREG($a0) # get the Register mask
jr $ra # return
#
# NoGC Garbage Collector
#
# NoGC does not attempt to do any garbage collection.
# It simply expands the heap if more memory is needed.
#
#
# Some constants
#
NoGC_EXPANDSIZE=0x10000 # size to expand heap
#
# Initialization
#
# INPUT:
# none
#
# OUTPUT:
# $gp: lower bound of the work area
# $s7: upper bound of the work area
#
# Registers modified:
# $a0, $v0
#
.globl _NoGC_Init
_NoGC_Init:
la $gp heap_start # set $gp to the start of the heap
li $v0 9 # get heap end
move $a0 $zero
syscall # sbrk
move $s7 $v0 # set limit pointer
jr $ra
#
# Collection
#
# Expand the heap as necessary.
#
# INPUT:
# $a1: size will need to allocate in bytes
# $s7: limit pointer of thw work area
# $gp: current allocation pointer
#
# OUTPUT:
# $a1: size will need to allocate in bytes (unchanged)
#
# Registers modified:
# $t0, $a0, $v0, $gp, $s7
#
.globl _NoGC_Collect
_NoGC_Collect:
la $a0 _NoGC_COLLECT # show collection message
li $v0 4
syscall
_NoGC_Collect_loop:
add $t0 $gp $a1 # test allocation
blt $t0 $s7 _NoGC_Collect_ok # stop if enough
li $v0 9 # expand heap
li $a0 NoGC_EXPANDSIZE # set the size to expand the heap
syscall # sbrk
li $v0 9 # get heap end
move $a0 $zero
syscall # sbrk
move $s7 $v0 # set limit pointer
b _NoGC_Collect_loop # loop
_NoGC_Collect_ok:
jr $ra # return