diff --git a/Makefile b/Makefile index 10efe34d34..49c08c7d13 100755 --- a/Makefile +++ b/Makefile @@ -38,7 +38,6 @@ DLL_OBJS = $(PLAF_DLL_OBJS) \ vm/byte_arrays.o \ vm/callstack.o \ vm/code_block.o \ - vm/code_gc.o \ vm/code_heap.o \ vm/contexts.o \ vm/data_gc.o \ @@ -47,6 +46,7 @@ DLL_OBJS = $(PLAF_DLL_OBJS) \ vm/dispatch.o \ vm/errors.o \ vm/factor.o \ + vm/heap.o \ vm/image.o \ vm/inline_cache.o \ vm/io.o \ diff --git a/vm/code_block.cpp b/vm/code_block.cpp index 2251345af7..bd7a93bf6d 100755 --- a/vm/code_block.cpp +++ b/vm/code_block.cpp @@ -371,7 +371,7 @@ void factor_vm::update_word_references(code_block *compiled) the code heap with dead PICs that will be freed on the next GC, we add them to the free list immediately. */ else if(compiled->type == PIC_TYPE) - heap_free(&code,compiled); + code->heap_free(compiled); else { iterate_relocations(compiled,factor::update_word_references_step); @@ -411,7 +411,7 @@ void factor_vm::mark_code_block(code_block *compiled) { check_code_address((cell)compiled); - mark_block(compiled); + code->mark_block(compiled); copy_handle(&compiled->literals); copy_handle(&compiled->relocation); @@ -503,19 +503,19 @@ void factor_vm::fixup_labels(array *labels, code_block *compiled) /* Might GC */ code_block *factor_vm::allot_code_block(cell size) { - heap_block *block = heap_allot(&code,size + sizeof(code_block)); + heap_block *block = code->heap_allot(size + sizeof(code_block)); /* If allocation failed, do a code GC */ if(block == NULL) { gc(); - block = heap_allot(&code,size + sizeof(code_block)); + block = code->heap_allot(size + sizeof(code_block)); /* Insufficient room even after code GC, give up */ if(block == NULL) { cell used, total_free, max_free; - heap_usage(&code,&used,&total_free,&max_free); + code->heap_usage(&used,&total_free,&max_free); print_string("Code heap stats:\n"); print_string("Used: "); print_cell(used); nl(); diff --git a/vm/code_gc.hpp b/vm/code_gc.hpp deleted file mode 100755 index d2cfba0cf4..0000000000 --- a/vm/code_gc.hpp +++ /dev/null @@ -1,38 +0,0 @@ -namespace factor -{ - -static const cell free_list_count = 16; -static const cell block_size_increment = 32; - -struct heap_free_list { - free_heap_block *small_blocks[free_list_count]; - free_heap_block *large_blocks; -}; - -struct heap { - segment *seg; - heap_free_list free; -}; - -typedef void (*heap_iterator)(heap_block *compiled,factor_vm *vm); - -inline static heap_block *next_block(heap *h, heap_block *block) -{ - cell next = ((cell)block + block->size); - if(next == h->seg->end) - return NULL; - else - return (heap_block *)next; -} - -inline static heap_block *first_block(heap *h) -{ - return (heap_block *)h->seg->start; -} - -inline static heap_block *last_block(heap *h) -{ - return (heap_block *)h->seg->end; -} - -} diff --git a/vm/code_heap.cpp b/vm/code_heap.cpp index 7d70b4c254..c1139234ed 100755 --- a/vm/code_heap.cpp +++ b/vm/code_heap.cpp @@ -6,12 +6,12 @@ namespace factor /* Allocate a code heap during startup */ void factor_vm::init_code_heap(cell size) { - new_heap(&code,size); + code = new heap(this,size); } bool factor_vm::in_code_heap_p(cell ptr) { - return (ptr >= code.seg->start && ptr <= code.seg->end); + return (ptr >= code->seg->start && ptr <= code->seg->end); } /* Compile a word definition with the non-optimizing compiler. Allocates memory */ @@ -31,13 +31,13 @@ void factor_vm::jit_compile_word(cell word_, cell def_, bool relocate) /* Apply a function to every code block */ void factor_vm::iterate_code_heap(code_heap_iterator iter) { - heap_block *scan = first_block(&code); + heap_block *scan = code->first_block(); while(scan) { if(scan->status != B_FREE) iter((code_block *)scan,this); - scan = next_block(&code,scan); + scan = code->next_block(scan); } } @@ -112,8 +112,8 @@ PRIMITIVE_FORWARD(modify_code_heap) inline void factor_vm::primitive_code_room() { cell used, total_free, max_free; - heap_usage(&code,&used,&total_free,&max_free); - dpush(tag_fixnum(code.seg->size / 1024)); + code->heap_usage(&used,&total_free,&max_free); + dpush(tag_fixnum(code->seg->size / 1024)); dpush(tag_fixnum(used / 1024)); dpush(tag_fixnum(total_free / 1024)); dpush(tag_fixnum(max_free / 1024)); @@ -220,20 +220,20 @@ void factor_vm::compact_code_heap() gc(); /* Figure out where the code heap blocks are going to end up */ - cell size = compute_heap_forwarding(&code, forwarding); + cell size = code->compute_heap_forwarding(forwarding); /* Update word and quotation code pointers */ forward_object_xts(); /* Actually perform the compaction */ - compact_heap(&code,forwarding); + code->compact_heap(forwarding); /* Update word and quotation XTs */ fixup_object_xts(); /* Now update the free list; there will be a single free block at the end */ - build_free_list(&code,size); + code->build_free_list(size); } } diff --git a/vm/code_heap.hpp b/vm/code_heap.hpp index 709ec85f95..f68c80a2a1 100755 --- a/vm/code_heap.hpp +++ b/vm/code_heap.hpp @@ -1,7 +1,8 @@ namespace factor { + struct factor_vm; -typedef void (*code_heap_iterator)(code_block *compiled,factor_vm *myvm); +typedef void (*code_heap_iterator)(code_block *compiled, factor_vm *myvm); PRIMITIVE(modify_code_heap); PRIMITIVE(code_room); diff --git a/vm/data_gc.cpp b/vm/data_gc.cpp index 18c38cade3..011cc1f5f3 100755 --- a/vm/data_gc.cpp +++ b/vm/data_gc.cpp @@ -509,7 +509,7 @@ void factor_vm::garbage_collection(cell gen,bool growing_data_heap_,cell request growing_data_heap = true; /* see the comment in unmark_marked() */ - unmark_marked(&code); + code->unmark_marked(); } /* we try collecting aging space twice before going on to collect tenured */ @@ -546,7 +546,7 @@ void factor_vm::garbage_collection(cell gen,bool growing_data_heap_,cell request code_heap_scans++; if(collecting_gen == data->tenured()) - free_unmarked(&code,(heap_iterator)factor::update_literal_and_word_references); + code->free_unmarked((heap_iterator)factor::update_literal_and_word_references); else copy_code_heap_roots(); diff --git a/vm/debug.cpp b/vm/debug.cpp index 1ec73760d3..8cacbeca47 100755 --- a/vm/debug.cpp +++ b/vm/debug.cpp @@ -297,7 +297,7 @@ void factor_vm::dump_code_heap() { cell reloc_size = 0, literal_size = 0; - heap_block *scan = first_block(&code); + heap_block *scan = code->first_block(); while(scan) { @@ -326,7 +326,7 @@ void factor_vm::dump_code_heap() print_cell_hex(scan->size); print_string(" "); print_string(status); print_string("\n"); - scan = next_block(&code,scan); + scan = code->next_block(scan); } print_cell(reloc_size); print_string(" bytes of relocation data\n"); diff --git a/vm/code_gc.cpp b/vm/heap.cpp old mode 100755 new mode 100644 similarity index 56% rename from vm/code_gc.cpp rename to vm/heap.cpp index 1c372368dd..0905d7c190 --- a/vm/code_gc.cpp +++ b/vm/heap.cpp @@ -1,37 +1,36 @@ #include "master.hpp" +/* This malloc-style heap code is reasonably generic. Maybe in the future, it +will be used for the data heap too, if we ever get mark/sweep/compact GC. */ + namespace factor { -void factor_vm::clear_free_list(heap *heap) +void heap::clear_free_list() { - memset(&heap->free,0,sizeof(heap_free_list)); + memset(&free,0,sizeof(heap_free_list)); } -/* This malloc-style heap code is reasonably generic. Maybe in the future, it -will be used for the data heap too, if we ever get incremental -mark/sweep/compact GC. */ -void factor_vm::new_heap(heap *heap, cell size) +heap::heap(factor_vm *myvm_, cell size) { - heap->seg = alloc_segment(align_page(size)); - if(!heap->seg) - fatal_error("Out of memory in new_heap",size); - - clear_free_list(heap); + myvm = myvm_; + seg = myvm->alloc_segment(myvm->align_page(size)); + if(!seg) fatal_error("Out of memory in new_heap",size); + clear_free_list(); } -void factor_vm::add_to_free_list(heap *heap, free_heap_block *block) +void heap::add_to_free_list(free_heap_block *block) { if(block->size < free_list_count * block_size_increment) { int index = block->size / block_size_increment; - block->next_free = heap->free.small_blocks[index]; - heap->free.small_blocks[index] = block; + block->next_free = free.small_blocks[index]; + free.small_blocks[index] = block; } else { - block->next_free = heap->free.large_blocks; - heap->free.large_blocks = block; + block->next_free = free.large_blocks; + free.large_blocks = block; } } @@ -39,16 +38,16 @@ void factor_vm::add_to_free_list(heap *heap, free_heap_block *block) In the former case, we must add a large free block from compiling.base + size to compiling.limit. */ -void factor_vm::build_free_list(heap *heap, cell size) +void heap::build_free_list(cell size) { heap_block *prev = NULL; - clear_free_list(heap); + clear_free_list(); size = (size + block_size_increment - 1) & ~(block_size_increment - 1); - heap_block *scan = first_block(heap); - free_heap_block *end = (free_heap_block *)(heap->seg->start + size); + heap_block *scan = first_block(); + free_heap_block *end = (free_heap_block *)(seg->start + size); /* Add all free blocks to the free list */ while(scan && scan < (heap_block *)end) @@ -56,28 +55,28 @@ void factor_vm::build_free_list(heap *heap, cell size) switch(scan->status) { case B_FREE: - add_to_free_list(heap,(free_heap_block *)scan); + add_to_free_list((free_heap_block *)scan); break; case B_ALLOCATED: break; default: - critical_error("Invalid scan->status",(cell)scan); + myvm->critical_error("Invalid scan->status",(cell)scan); break; } prev = scan; - scan = next_block(heap,scan); + scan = next_block(scan); } /* If there is room at the end of the heap, add a free block. This branch is only taken after loading a new image, not after code GC */ - if((cell)(end + 1) <= heap->seg->end) + if((cell)(end + 1) <= seg->end) { end->status = B_FREE; - end->size = heap->seg->end - (cell)end; + end->size = seg->end - (cell)end; /* add final free block */ - add_to_free_list(heap,end); + add_to_free_list(end); } /* This branch is taken if the newly loaded image fits exactly, or after code GC */ @@ -86,30 +85,30 @@ void factor_vm::build_free_list(heap *heap, cell size) /* even if there's no room at the end of the heap for a new free block, we might have to jigger it up by a few bytes in case prev + prev->size */ - if(prev) prev->size = heap->seg->end - (cell)prev; + if(prev) prev->size = seg->end - (cell)prev; } } -void factor_vm::assert_free_block(free_heap_block *block) +void heap::assert_free_block(free_heap_block *block) { if(block->status != B_FREE) - critical_error("Invalid block in free list",(cell)block); + myvm->critical_error("Invalid block in free list",(cell)block); } -free_heap_block *factor_vm::find_free_block(heap *heap, cell size) +free_heap_block *heap::find_free_block(cell size) { cell attempt = size; while(attempt < free_list_count * block_size_increment) { int index = attempt / block_size_increment; - free_heap_block *block = heap->free.small_blocks[index]; + free_heap_block *block = free.small_blocks[index]; if(block) { assert_free_block(block); - heap->free.small_blocks[index] = block->next_free; + free.small_blocks[index] = block->next_free; return block; } @@ -117,7 +116,7 @@ free_heap_block *factor_vm::find_free_block(heap *heap, cell size) } free_heap_block *prev = NULL; - free_heap_block *block = heap->free.large_blocks; + free_heap_block *block = free.large_blocks; while(block) { @@ -127,7 +126,7 @@ free_heap_block *factor_vm::find_free_block(heap *heap, cell size) if(prev) prev->next_free = block->next_free; else - heap->free.large_blocks = block->next_free; + free.large_blocks = block->next_free; return block; } @@ -138,7 +137,7 @@ free_heap_block *factor_vm::find_free_block(heap *heap, cell size) return NULL; } -free_heap_block *factor_vm::split_free_block(heap *heap, free_heap_block *block, cell size) +free_heap_block *heap::split_free_block(free_heap_block *block, cell size) { if(block->size != size ) { @@ -148,21 +147,21 @@ free_heap_block *factor_vm::split_free_block(heap *heap, free_heap_block *block, split->size = block->size - size; split->next_free = block->next_free; block->size = size; - add_to_free_list(heap,split); + add_to_free_list(split); } return block; } /* Allocate a block of memory from the mark and sweep GC heap */ -heap_block *factor_vm::heap_allot(heap *heap, cell size) +heap_block *heap::heap_allot(cell size) { size = (size + block_size_increment - 1) & ~(block_size_increment - 1); - free_heap_block *block = find_free_block(heap,size); + free_heap_block *block = find_free_block(size); if(block) { - block = split_free_block(heap,block,size); + block = split_free_block(block,size); block->status = B_ALLOCATED; return block; @@ -172,13 +171,13 @@ heap_block *factor_vm::heap_allot(heap *heap, cell size) } /* Deallocates a block manually */ -void factor_vm::heap_free(heap *heap, heap_block *block) +void heap::heap_free(heap_block *block) { block->status = B_FREE; - add_to_free_list(heap,(free_heap_block *)block); + add_to_free_list((free_heap_block *)block); } -void factor_vm::mark_block(heap_block *block) +void heap::mark_block(heap_block *block) { /* If already marked, do nothing */ switch(block->status) @@ -189,41 +188,41 @@ void factor_vm::mark_block(heap_block *block) block->status = B_MARKED; break; default: - critical_error("Marking the wrong block",(cell)block); + myvm->critical_error("Marking the wrong block",(cell)block); break; } } /* If in the middle of code GC, we have to grow the heap, data GC restarts from scratch, so we have to unmark any marked blocks. */ -void factor_vm::unmark_marked(heap *heap) +void heap::unmark_marked() { - heap_block *scan = first_block(heap); + heap_block *scan = first_block(); while(scan) { if(scan->status == B_MARKED) scan->status = B_ALLOCATED; - scan = next_block(heap,scan); + scan = next_block(scan); } } /* After code GC, all referenced code blocks have status set to B_MARKED, so any which are allocated and not marked can be reclaimed. */ -void factor_vm::free_unmarked(heap *heap, heap_iterator iter) +void heap::free_unmarked(heap_iterator iter) { - clear_free_list(heap); + clear_free_list(); heap_block *prev = NULL; - heap_block *scan = first_block(heap); + heap_block *scan = first_block(); while(scan) { switch(scan->status) { case B_ALLOCATED: - if(secure_gc) + if(myvm->secure_gc) memset(scan + 1,0,scan->size - sizeof(heap_block)); if(prev && prev->status == B_FREE) @@ -242,30 +241,30 @@ void factor_vm::free_unmarked(heap *heap, heap_iterator iter) break; case B_MARKED: if(prev && prev->status == B_FREE) - add_to_free_list(heap,(free_heap_block *)prev); + add_to_free_list((free_heap_block *)prev); scan->status = B_ALLOCATED; prev = scan; - iter(scan,this); + iter(scan,myvm); break; default: - critical_error("Invalid scan->status",(cell)scan); + myvm->critical_error("Invalid scan->status",(cell)scan); } - scan = next_block(heap,scan); + scan = next_block(scan); } if(prev && prev->status == B_FREE) - add_to_free_list(heap,(free_heap_block *)prev); + add_to_free_list((free_heap_block *)prev); } /* Compute total sum of sizes of free blocks, and size of largest free block */ -void factor_vm::heap_usage(heap *heap, cell *used, cell *total_free, cell *max_free) +void heap::heap_usage(cell *used, cell *total_free, cell *max_free) { *used = 0; *total_free = 0; *max_free = 0; - heap_block *scan = first_block(heap); + heap_block *scan = first_block(); while(scan) { @@ -280,34 +279,34 @@ void factor_vm::heap_usage(heap *heap, cell *used, cell *total_free, cell *max_f *max_free = scan->size; break; default: - critical_error("Invalid scan->status",(cell)scan); + myvm->critical_error("Invalid scan->status",(cell)scan); } - scan = next_block(heap,scan); + scan = next_block(scan); } } /* The size of the heap, not including the last block if it's free */ -cell factor_vm::heap_size(heap *heap) +cell heap::heap_size() { - heap_block *scan = first_block(heap); + heap_block *scan = first_block(); - while(next_block(heap,scan) != NULL) - scan = next_block(heap,scan); + while(next_block(scan) != NULL) + scan = next_block(scan); /* this is the last block in the heap, and it is free */ if(scan->status == B_FREE) - return (cell)scan - heap->seg->start; + return (cell)scan - seg->start; /* otherwise the last block is allocated */ else - return heap->seg->size; + return seg->size; } /* Compute where each block is going to go, after compaction */ -cell factor_vm::compute_heap_forwarding(heap *heap, unordered_map &forwarding) +cell heap::compute_heap_forwarding(unordered_map &forwarding) { - heap_block *scan = first_block(heap); - char *address = (char *)first_block(heap); + heap_block *scan = first_block(); + char *address = (char *)first_block(); while(scan) { @@ -317,21 +316,21 @@ cell factor_vm::compute_heap_forwarding(heap *heap, unordered_mapsize; } else if(scan->status == B_MARKED) - critical_error("Why is the block marked?",0); + myvm->critical_error("Why is the block marked?",0); - scan = next_block(heap,scan); + scan = next_block(scan); } - return (cell)address - heap->seg->start; + return (cell)address - seg->start; } -void factor_vm::compact_heap(heap *heap, unordered_map &forwarding) +void heap::compact_heap(unordered_map &forwarding) { - heap_block *scan = first_block(heap); + heap_block *scan = first_block(); while(scan) { - heap_block *next = next_block(heap,scan); + heap_block *next = next_block(scan); if(scan->status == B_ALLOCATED) memmove(forwarding[scan],scan,scan->size); diff --git a/vm/heap.hpp b/vm/heap.hpp new file mode 100644 index 0000000000..ab1cfeef6d --- /dev/null +++ b/vm/heap.hpp @@ -0,0 +1,59 @@ +namespace factor +{ + +static const cell free_list_count = 16; +static const cell block_size_increment = 32; + +struct heap_free_list { + free_heap_block *small_blocks[free_list_count]; + free_heap_block *large_blocks; +}; + +typedef void (*heap_iterator)(heap_block *compiled, factor_vm *vm); + +struct heap { + factor_vm *myvm; + segment *seg; + heap_free_list free; + + heap(factor_vm *myvm, cell size); + + inline heap_block *next_block(heap_block *block) + { + cell next = ((cell)block + block->size); + if(next == seg->end) + return NULL; + else + return (heap_block *)next; + } + + inline heap_block *first_block() + { + return (heap_block *)seg->start; + } + + inline heap_block *last_block() + { + return (heap_block *)seg->end; + } + + void clear_free_list(); + void new_heap(cell size); + void add_to_free_list(free_heap_block *block); + void build_free_list(cell size); + void assert_free_block(free_heap_block *block); + free_heap_block *find_free_block(cell size); + free_heap_block *split_free_block(free_heap_block *block, cell size); + heap_block *heap_allot(cell size); + void heap_free(heap_block *block); + void mark_block(heap_block *block); + void unmark_marked(); + void free_unmarked(heap_iterator iter); + void heap_usage(cell *used, cell *total_free, cell *max_free); + cell heap_size(); + cell compute_heap_forwarding(unordered_map &forwarding); + void compact_heap(unordered_map &forwarding); + +}; + +} diff --git a/vm/image.cpp b/vm/image.cpp index ade654c2e2..14bd0926b9 100755 --- a/vm/image.cpp +++ b/vm/image.cpp @@ -56,7 +56,7 @@ void factor_vm::load_code_heap(FILE *file, image_header *h, vm_parameters *p) if(h->code_size != 0) { - size_t bytes_read = fread(first_block(&code),1,h->code_size,file); + size_t bytes_read = fread(code->first_block(),1,h->code_size,file); if(bytes_read != h->code_size) { print_string("truncated image: "); @@ -69,7 +69,7 @@ void factor_vm::load_code_heap(FILE *file, image_header *h, vm_parameters *p) } code_relocation_base = h->code_relocation_base; - build_free_list(&code,h->code_size); + code->build_free_list(h->code_size); } /* Save the current image to disk */ @@ -92,8 +92,8 @@ bool factor_vm::save_image(const vm_char *filename) h.version = image_version; h.data_relocation_base = tenured->start; h.data_size = tenured->here - tenured->start; - h.code_relocation_base = code.seg->start; - h.code_size = heap_size(&code); + h.code_relocation_base = code->seg->start; + h.code_size = code->heap_size(); h.t = T; h.bignum_zero = bignum_zero; @@ -107,7 +107,7 @@ bool factor_vm::save_image(const vm_char *filename) if(fwrite(&h,sizeof(image_header),1,file) != 1) ok = false; if(fwrite((void*)tenured->start,h.data_size,1,file) != 1) ok = false; - if(fwrite(first_block(&code),h.code_size,1,file) != 1) ok = false; + if(fwrite(code->first_block(),h.code_size,1,file) != 1) ok = false; if(fclose(file)) ok = false; if(!ok) @@ -175,7 +175,7 @@ void data_fixup(cell *cell, factor_vm *myvm) template void factor_vm::code_fixup(TYPE **handle) { TYPE *ptr = *handle; - TYPE *new_ptr = (TYPE *)(((cell)ptr) + (code.seg->start - code_relocation_base)); + TYPE *new_ptr = (TYPE *)(((cell)ptr) + (code->seg->start - code_relocation_base)); *handle = new_ptr; } diff --git a/vm/inline_cache.cpp b/vm/inline_cache.cpp index 71076821b5..39147d0570 100755 --- a/vm/inline_cache.cpp +++ b/vm/inline_cache.cpp @@ -24,7 +24,7 @@ void factor_vm::deallocate_inline_cache(cell return_address) #endif if(old_type == PIC_TYPE) - heap_free(&code,old_block); + code->heap_free(old_block); } /* Figure out what kind of type check the PIC needs based on the methods diff --git a/vm/master.hpp b/vm/master.hpp index e4df60a8a2..e51273f546 100755 --- a/vm/master.hpp +++ b/vm/master.hpp @@ -64,7 +64,7 @@ #include "math.hpp" #include "float_bits.hpp" #include "io.hpp" -#include "code_gc.hpp" +#include "heap.hpp" #include "code_heap.hpp" #include "image.hpp" #include "callstack.hpp" diff --git a/vm/vm-data.hpp b/vm/vm-data.hpp index f4faf5b46b..7afea3c876 100644 --- a/vm/vm-data.hpp +++ b/vm/vm-data.hpp @@ -83,8 +83,8 @@ struct factor_vm_data { cell bignum_neg_one; //code_heap - heap code; - unordered_map forwarding; + heap *code; + unordered_map forwarding; //image cell code_relocation_base; diff --git a/vm/vm.hpp b/vm/vm.hpp index 282f48b41e..056393edb4 100644 --- a/vm/vm.hpp +++ b/vm/vm.hpp @@ -381,24 +381,6 @@ struct factor_vm : factor_vm_data { inline void primitive_fflush(); inline void primitive_fclose(); - //code_gc - void clear_free_list(heap *heap); - void new_heap(heap *heap, cell size); - void add_to_free_list(heap *heap, free_heap_block *block); - void build_free_list(heap *heap, cell size); - void assert_free_block(free_heap_block *block); - free_heap_block *find_free_block(heap *heap, cell size); - free_heap_block *split_free_block(heap *heap, free_heap_block *block, cell size); - heap_block *heap_allot(heap *heap, cell size); - void heap_free(heap *heap, heap_block *block); - void mark_block(heap_block *block); - void unmark_marked(heap *heap); - void free_unmarked(heap *heap, heap_iterator iter); - void heap_usage(heap *heap, cell *used, cell *total_free, cell *max_free); - cell heap_size(heap *heap); - cell compute_heap_forwarding(heap *heap, unordered_map &forwarding); - void compact_heap(heap *heap, unordered_map &forwarding); - //code_block relocation_type relocation_type_of(relocation_entry r); relocation_class relocation_class_of(relocation_entry r);