DIRECTORY Allocator: TYPE USING [bsiEscape, ExtendedHeader, HeaderP, NHeaderP, QuantumIndex, wordsPerQuantum, LastAddress, NormalHeader], AllocatorOps: TYPE USING [EnterAndCallBack, NHPToREF, REFToNHP, TAndSFreeObject, IsValidRef, quantumMap, Reset, BlockSize], Basics: TYPE USING [BITAND, LowHalf], Collector: TYPE USING [EstablishTAndSProc], DebuggerSwap: TYPE USING [CallDebugger], LoadState: TYPE USING [Acquire, Release, local, EnumerateAllModules, ConfigID, ModuleIndex, ModuleToGlobalFrame, GlobalFrameToType], PrincOps: TYPE USING[GlobalFrameHandle, wordsPerPage], RCMap: TYPE USING [nullIndex], RCMicrocodeOps: TYPE USING [OnZ, ZCTFull, RCOverflowOccurred, RCUnderflowOccurred, ASSIGNREF], RTFrameHeapSnapshot: TYPE USING [AcquireFrameHeapSnapshot, ReleaseFrameHeapSnapshot, MapUncountedBodies], RTTypesBasicPrivate: TYPE USING [MapRefs, MapTiRcmx, NumberPackageRefs], SafeStorage: TYPE USING [nullType, Type, GetReferentType, ReclaimCollectibleObjects], VM: TYPE USING [AddressForPageNumber, Allocate, Interval, Free, PageNumberForAddress], ZCT: TYPE USING [EnterAndCallBack, EnterRCOvAndCallBack, InnerHandleRCOverflow, InnerHandleRCUnderflow, zct, zctBlockWords, RCOvReset]; TraceAndSweepImpl: PROGRAM IMPORTS AllocatorOps, Basics, Collector, DebuggerSwap, LoadState, RCMicrocodeOps, RTFrameHeapSnapshot, RTTypesBasicPrivate, SafeStorage, VM, ZCT = BEGIN OPEN Allocator, AllocatorOps, SafeStorage; nRetainedObjects: LONG CARDINAL _ 0; rpa: ARRAY [0..maxNRPIndex] OF LONG POINTER _ ALL[NIL]; maxNRPIndex: CARDINAL = 20; nextRpaIndex: CARDINAL _ 0; destructiveTestEnabled: BOOL _ FALSE; findingCircularGarbage: BOOL _ FALSE; refStackChain: RefStack _ NIL; refStackFree: RefStack _ NIL; nRefsPushed: INT _ 0; objectsSeen: LONG CARDINAL _ 0; objectsReclaimed: LONG CARDINAL _ 0; objectsKept: LONG CARDINAL _ 0; nGlobalFrames: LONG CARDINAL _ 0; nRCGlobalFrames: LONG CARDINAL _ 0; nLocalFrames: LONG CARDINAL _ 0; SuspectSeen: SIGNAL = CODE; suspect: LONG POINTER TO UNSPECIFIED _ LOOPHOLE[LONG[-1]]; RefStackRep: TYPE = RECORD [ next: RefStack, size, max: CARDINAL, refs: SEQUENCE COMPUTED CARDINAL OF LONG POINTER ]; RefStack: TYPE = LONG POINTER TO RefStackRep; suspectRef: LONG POINTER TO UNSPECIFIED _ NIL; -- a LOOPHOLE'd REF APNodes: TYPE = ARRAY [0..maxNReferers) OF NHeaderP; suspectReferers: REF APNodes _ NEW[APNodes _ ALL[NIL]]; maxNReferers: NAT = 10; nSuspectReferers: NAT _ 0; AIPNodes: TYPE = ARRAY [0..maxNInnerReferers) OF NHeaderP; innerReferers: REF AIPNodes _ NEW[AIPNodes _ ALL[NIL]]; maxNInnerReferers: NAT = 50; nInnerReferers: NAT _ 0; gfhToSuspectRef: PrincOps.GlobalFrameHandle _ NIL; suspectRefFoundInLocalFrame: BOOL _ FALSE; circularStructureFound: BOOL _ FALSE; WhoPointsTo: PROC [nhp: NHeaderP, cycleLimit: NAT _ 20] RETURNS[ referers: REF APNodes, gfh: PrincOps.GlobalFrameHandle, foundInLocalFrame, foundInCircularStructure: BOOL, cycles: NAT _ 0] = { FOR i: NAT IN [0..maxNReferers) DO suspectReferers[i] _ NIL ENDLOOP; suspectReferers[0] _ nhp; nSuspectReferers _ 1; FOR i: NAT IN [0..maxNInnerReferers) DO innerReferers[i] _ NIL ENDLOOP; nInnerReferers _ 0; gfhToSuspectRef _ NIL; suspectRefFoundInLocalFrame _ FALSE; circularStructureFound _ FALSE; UNTIL gfhToSuspectRef # NIL OR circularStructureFound OR suspectRefFoundInLocalFrame OR nSuspectReferers > 1 OR nSuspectReferers = 0 OR cycles = cycleLimit DO suspectRef _ LOOPHOLE[NHPToREF[suspectReferers[0]], LONG POINTER]; FOR i: NAT IN [0..maxNReferers) DO suspectReferers[i] _ NIL ENDLOOP; nSuspectReferers _ 0; SafeStorage.ReclaimCollectibleObjects[suspendMe: TRUE, traceAndSweep: TRUE]; cycles _ cycles + 1; ENDLOOP; suspectRef _ NIL; RETURN[ referers: suspectReferers, gfh: gfhToSuspectRef, foundInLocalFrame: suspectRefFoundInLocalFrame, foundInCircularStructure: circularStructureFound, cycles: cycles]; }; FindCircularGarbage: PROC = { SafeStorage.ReclaimCollectibleObjects[suspendMe: TRUE]; findingCircularGarbage _ TRUE; SafeStorage.ReclaimCollectibleObjects[suspendMe: TRUE, traceAndSweep: TRUE]; findingCircularGarbage _ FALSE; }; IncRC: PROC [ref: REF] = { dummy: REF _ NIL; RCMicrocodeOps.ASSIGNREF[rhs: ref, lhs: @dummy ! RCMicrocodeOps.RCOverflowOccurred => {ZCT.InnerHandleRCOverflow[ref]; RETRY}; ]; dummy _ NIL; }; DecRC: PROC [ref: REF] = { dummy: REF _ ref; RCMicrocodeOps.ASSIGNREF[rhs: NIL, lhs: @dummy ! RCMicrocodeOps.RCUnderflowOccurred => {ZCT.InnerHandleRCUnderflow[ref]; RETRY}]; dummy _ NIL; }; ActuallyDoIt: PROC[ignoreStack: BOOL _ FALSE] = { clearRC: PROC [nhp: NHeaderP] = { objectsSeen _ objectsSeen + 1; nhp.inZCT _ FALSE; nhp.maybeOnStack _ FALSE; nhp.refCount _ 0; nhp.rcOverflowed _ FALSE; }; objectsSeen _ 0; objectsKept _ objectsReclaimed _ 0; nRCGlobalFrames _ nGlobalFrames _ nLocalFrames _ 0; nRetainedObjects _ 0; nextRpaIndex _ 0; nRefsPushed _ 0; ZCT.zct.bsiToFreeList _ ALL[NIL]; AllocatorOps.Reset[]; ZCT.zct.markingDecrements _ FALSE; ZCT.zct.rp _ ZCT.zct.wp _ ZCT.zct.rp - Basics.BITAND[ Basics.LowHalf[LOOPHOLE[ZCT.zct.rp, LONG CARDINAL]], ZCT.zctBlockWords-1 ]; ZCT.RCOvReset[]; AllObjects[clearRC]; IF NOT ignoreStack THEN RTFrameHeapSnapshot.MapUncountedBodies[TraceLocalFrame]; [] _ LoadState.local.EnumerateAllModules[order: newestFirst, proc: TraceGlobalFrame]; UNTIL EmptyRefStack[] DO TraceRefsInObject[LOOPHOLE[PopRef[], REF]] ENDLOOP; FreeRefStacks[]; AllObjects[visitOneObject]; IF objectsReclaimed + objectsKept # objectsSeen THEN ERROR; -- check for consistency }; -- end ActuallyDoIt DoTraceAndSweepCollection: PROC = {ENABLE UNWIND => NULL; haveAllocatorLocked: PROC = { -- here with the loader and allocator locked haveRCOvLocked: PROC = { haveZCTLocked: PROC = { RTFrameHeapSnapshot.AcquireFrameHeapSnapshot[]; { ENABLE UNWIND => RTFrameHeapSnapshot.ReleaseFrameHeapSnapshot[]; ActuallyDoIt[ignoreStack: FALSE]; IF destructiveTestEnabled THEN { ActuallyDoIt[ignoreStack: TRUE]; DebuggerSwap.CallDebugger["destructive collection finished."]; }; -- look at XXX from the debugger RTFrameHeapSnapshot.ReleaseFrameHeapSnapshot[]; }; }; ZCT.EnterAndCallBack[haveZCTLocked]; }; ZCT.EnterRCOvAndCallBack[haveRCOvLocked]; }; -- end haveAllocatorLocked LoadState.local.Acquire[exclusive]; AllocatorOps.EnterAndCallBack[haveAllocatorLocked ! UNWIND => LoadState.local.Release[]]; LoadState.local.Release[]; }; -- end DoTraceAndSweepCollection MarkRef: PROC [ref: REF, countIt: BOOL _ TRUE] = INLINE { IF NOT Marked[ref] THEN { SetMark[ref]; IF RefContaining[ref] THEN PushRef[LOOPHOLE[ref, LONG POINTER]]; }; IF countIt THEN IncRC[ref] }; visitOneObject: PROC [nhp: NHeaderP] = { IF nhp.f THEN { -- adjust refCount FOR i: CARDINAL IN [1..RTTypesBasicPrivate.NumberPackageRefs[nhp.type]] DO DecRC[NHPToREF[nhp] ! RCMicrocodeOps.ZCTFull => DebuggerSwap.CallDebugger["TAndS disaster."]]; ENDLOOP; }; IF nhp.f OR Marked[AllocatorOps.NHPToREF[nhp]] THEN { objectsKept _ objectsKept + 1; IF NOT findingCircularGarbage THEN ClearMark[nhp]; IF nhp.refCount = 0 AND NOT nhp.rcOverflowed THEN { IF NOT nhp.f THEN { rpa[nextRpaIndex] _ LOOPHOLE[NHPToREF[nhp], LONG POINTER]; IF nextRpaIndex < maxNRPIndex THEN nextRpaIndex _ nextRpaIndex + 1; nRetainedObjects _ nRetainedObjects + 1; }; RCMicrocodeOps.OnZ[nhp ! RCMicrocodeOps.ZCTFull => DebuggerSwap.CallDebugger["TAndS disaster."]]; }; } ELSE { IF findingCircularGarbage THEN {objectsKept _ objectsKept + 1; RETURN}; objectsReclaimed _ objectsReclaimed + 1; AllocatorOps.TAndSFreeObject[nhp]; }; }; TraceLocalFrame: PROC [d: LONG DESCRIPTOR FOR ARRAY OF WORD] = { pa: LONG POINTER TO LONG CARDINAL = LOOPHOLE[BASE[d], LONG POINTER TO LONG CARDINAL]; nWords: CARDINAL = LENGTH[d]; nLocalFrames _ nLocalFrames + 1; IF nWords >= SIZE[REF] THEN FOR i: CARDINAL IN [0..nWords-SIZE[REF]] DO addr: LONG CARDINAL = (pa+i)^; IF addr # 0 AND AllocatorOps.IsValidRef[LOOPHOLE[addr, LONG POINTER]] THEN { ref: REF = LOOPHOLE[addr, REF]; IF suspectRef # NIL AND LOOPHOLE[addr, LONG POINTER] = suspectRef THEN suspectRefFoundInLocalFrame _ TRUE; MarkRef[ref: ref, countIt: FALSE]; }; ENDLOOP; }; TraceGlobalFrame: PROC [configID: LoadState.ConfigID, moduleIndex: LoadState.ModuleIndex] RETURNS[stop: BOOL _ FALSE] = { gfh: PrincOps.GlobalFrameHandle = LoadState.local.ModuleToGlobalFrame[configID, moduleIndex]; type: Type _ LoadState.local.GlobalFrameToType[gfh]; procRef: PROC [ref: REF] = { IF ref = NIL THEN RETURN; IF suspectRef # NIL AND LOOPHOLE[ref, LONG POINTER] = suspectRef THEN gfhToSuspectRef _ gfh; MarkRef[ref]; }; nGlobalFrames _ nGlobalFrames + 1; IF type # nullType THEN { nRCGlobalFrames _ nRCGlobalFrames + 1; RTTypesBasicPrivate.MapRefs [LONG[gfh], RTTypesBasicPrivate.MapTiRcmx[type], procRef]; }; RETURN[FALSE]}; TraceRefsInObject: PROC [container: REF] = { type: Type _ GetReferentType[container]; refererPushed: BOOL _ FALSE; procRef: PROC [ref: REF] = { IF ref = NIL THEN RETURN; IF suspectRef # NIL AND LOOPHOLE[ref, LONG POINTER] = suspectRef AND NOT refererPushed THEN { nhp: NHeaderP = AllocatorOps.REFToNHP[container]; refererPushed _ TRUE; IF nSuspectReferers < maxNReferers THEN { suspectReferers[nSuspectReferers] _ nhp; nSuspectReferers _ nSuspectReferers + 1; }; FOR i: NAT IN [0..nInnerReferers) DO IF nhp = innerReferers[i] THEN {circularStructureFound _ TRUE; EXIT} ENDLOOP; IF NOT circularStructureFound AND nInnerReferers < maxNInnerReferers THEN { innerReferers[nInnerReferers] _ nhp; nInnerReferers _ nInnerReferers + 1; }; }; MarkRef[ref]; }; -- end procRef DO oldNRefsPushed: INT = nRefsPushed; RTTypesBasicPrivate.MapRefs [LOOPHOLE[container, LONG POINTER], RTTypesBasicPrivate.MapTiRcmx[type], procRef]; IF nRefsPushed-oldNRefsPushed # 1 THEN RETURN; container _ LOOPHOLE[PopRef[], REF]; type _ GetReferentType[container]; refererPushed _ FALSE; ENDLOOP; }; AllObjects: PROC [visit: PROC [NHeaderP]] = { qi: QuantumIndex _ FIRST[QuantumIndex]; DO hp: HeaderP; blockSize: INT _ 0; UNTIL AllocatorOps.quantumMap[qi] DO IF qi = LAST[QuantumIndex] THEN RETURN; qi _ qi+1; ENDLOOP; FOR hp _ QuantumIndexToLP[qi], hp + blockSize WHILE LOOPHOLE[hp, LONG CARDINAL] < LastAddress AND quantumMap[LPToQuantumIndex[hp]] DO nhp: NHeaderP = LOOPHOLE [ IF IsExtendedBlock[hp] THEN hp + SIZE[ExtendedHeader] - SIZE[NormalHeader] ELSE hp, NHeaderP ]; blockSize _ BlockSize[hp]; visit[nhp]; ENDLOOP; IF LOOPHOLE[hp, LONG CARDINAL] >= LastAddress THEN RETURN; qi _ LPToQuantumIndex[hp]; ENDLOOP; }; LPToQuantumIndex: PROC[lp: LONG POINTER] RETURNS[QuantumIndex] = { RETURN[LOOPHOLE[lp, LONG CARDINAL]/wordsPerQuantum]; }; QuantumIndexToLP: PROC[qi: QuantumIndex] RETURNS[LONG POINTER] = { n: LONG CARDINAL _ qi; RETURN[LOOPHOLE[n*wordsPerQuantum, LONG POINTER]]; }; IsExtendedBlock: PROC[hp: HeaderP] RETURNS[BOOL] = { RETURN[LOOPHOLE[hp, NHeaderP].blockSizeIndex = bsiEscape]; }; Marked: PROC [ref: REF] RETURNS [BOOL] = INLINE { RETURN [REFToNHP[ref].inZCT--i.e. marked--]}; SetMark: PROC [ref: REF] = INLINE { REFToNHP[ref].inZCT--i.e. marked-- _ TRUE}; ClearMark: PROC [nhp: NHeaderP] = INLINE { nhp.inZCT--i.e. marked-- _ FALSE}; PushRef: PROC [pref: LONG POINTER] = { stack: RefStack _ refStackChain; IF stack = NIL OR stack.size = stack.max THEN { IF refStackFree = NIL THEN { stack _ VM.AddressForPageNumber[VM.Allocate[count: 1].page]; stack.next _ NIL; stack.size _ 0; stack.max _ (PrincOps.wordsPerPage - (SIZE[RefStackRep])) / (SIZE[LONG POINTER]); } ELSE { stack _ refStackFree; refStackFree _ stack.next; }; stack.next _ refStackChain; refStackChain _ stack; }; stack[stack.size] _ pref; stack.size _ stack.size + 1; nRefsPushed _ nRefsPushed + 1; }; PopRef: PROC RETURNS [pref: LONG POINTER] = { stack: RefStack _ refStackChain; IF stack # NIL THEN { size: CARDINAL _ stack.size; IF size = 0 THEN { refStackChain _ stack.next; stack.next _ refStackFree; refStackFree _ stack; IF (stack _ refStackChain) = NIL THEN RETURN [NIL]; IF (size _ stack.size) = 0 THEN ERROR; }; size _ size - 1; stack.size _ size; RETURN [stack[size]]; }; RETURN [NIL]; }; EmptyRefStack: PROC RETURNS [BOOL] = INLINE { RETURN [refStackChain = NIL]}; FreeRefStacks: PROC = { IF refStackChain # NIL THEN ERROR; -- should never happen, but check anyway WHILE refStackFree # NIL DO stack: RefStack _ refStackFree.next; VM.Free[[page: VM.PageNumberForAddress[refStackFree], count: 1]]; refStackFree _ stack ENDLOOP; }; RefContaining: PROC [ref: REF] RETURNS [BOOL] = INLINE { IF ref = NIL THEN RETURN[FALSE]; RETURN [RTTypesBasicPrivate.MapTiRcmx[GetReferentType[ref]] # RCMap.nullIndex]; }; Collector.EstablishTAndSProc[DoTraceAndSweepCollection] END. .TraceAndSweepImpl.mesa last change by Paul Rovner, December 9, 1983 4:51 pm last change by Richard Koo, July 3, 1984 1:04:20 pm PDT This module implements a trace-and-sweep garbage collector for Cedar ("DoTraceAndSweepCollection"). It uses auxilliary storage for a reference stack. To allow subsequent incremental collection, DoTraceAndSweepCollection restores reference counts to correct values. No collectible storage is allocated while DoTraceAndSweepCollection is active, nor are any REFs change in counted storage during that time. Client processes that attempt to do so are suspended until after DoTraceAndSweepCollection finishes. Variables number of objects retained only because REFs appear onstack destructiveTestEnabled TRUE => do a second TandS without the stack scan, then punt findingCircularGarbage TRUE => remember stuff found; don't reclaim it interesting statistics debugging aids TYPEs and constants support for WhoPointsTo and FindCircularGarbage PROCS Look up the referer chain. Stop if a gfh found, or if foundInLocalFrame, or if a loop is found, or if more than 1 referer is found, or if no referers are found (changed conserv scan) This guy first does a standard incremental collection. Then it goes thru the motions of a TandS, but does not reclaim any garbage. Instead, it leaves the marked bit set. NOTE that this works only if SSExtra.useSizeToZn is TRUE. BEWARE don't forget to clear marks later! XXX local space management Leave the f flag and the type field alone Initialize Clear all free lists Clear the zero-count table mask rp by (LastAddress-(zctBlockWords-1)) Clear all ref counts and count objects trace from all local and global frames in the world continue marking from the ref stack and from objects free the space for ref stacks Free the objects Reclaim unmarked and free stuff, rebuild the zct and remember retained stuff due to frame heap only XXX make use of ordered enumeration to merge and trim free blocks here with the loader, allocator and RCOvBank locked here with the loader, allocator, RCOvBank and ZCT locked START haveRCOvLocked HERE Acquire the lock on the ref counting machinery, do the work, then release the lock. START haveAllocatorLocked HERE Acquire the lock on the RCOverflowBank, do the work, then release the lock. START DoTraceAndSweepCollection HERE First, acquire the lock on the loader (REFs in GF's are counted) Next, acquire the lock on the allocator, do the work, then release the lock. Release the loader's lock this is called once for each valid REF found in the frameheap or in counted storage (this includes reconstructing the RC for the object) here if this REF is valid and not seen before Reclaim unmarked and free stuff, rebuild the zct and remember retained stuff due to only frame heap put it on zct this procedure is used to mark refs in a local frame RCs must be decremented on first encounter if this REF is valid, non-NIL this procedure is used to mark refs from a global frame the algorithm is essentially the same as for regular objects applies P to each reference in the indicated object (ref) this proc visits all collectible objects (free AND allocated) Find the beginning of the next run of quanta from the quantum map Start parsing at the beginning of this run determines whether ref has been seen marks object as being seen clears object mark to ground state pushes ref to object onto the reference stack time to get a new stack node oh well, nothing comes for free pops ref to object from the reference stack tests reference stack for emptiness tests for object being ref-containing START TraceAndSweepImpl HERE Êä˜J˜Jšœ™Jšœ4™4Jšœ7™7J˜Jšœý™ýJ˜šÏk ˜ Jšœ œœk˜€Jšœœœd˜|Jšœœœœ ˜&Jšœ œœ˜,Jšœœœ˜)Jšœ œœo˜„Jšœ œœ"˜6Jšœœœ ˜JšœœœD˜^šœœ˜J˜J—Jšœœœ*˜IJšœ œœ>˜UJšœœœH˜VJšœœœœd˜‡J˜—šœ˜Jšœ‚˜Jšœœœ&˜2J˜Jšœ ™ J˜šœœœ˜$Jšœ;™;—Jš œœœœœœœ˜7Jšœ œ˜Jšœœ˜J˜šœœœ˜&JšœR™R—šœœœ˜&JšœE™EJ˜—Jšœœ˜Jšœœ˜Jšœ œ˜˜Jšœ™—Jšœ œœ˜Jšœœœ˜%Jšœ œœ˜ Jšœœœ˜"Jšœœœ˜$Jšœœœ˜!J˜Jšœ™Jšœ œœ˜Jš œ œœœ œœœ˜:J˜Jšœ™J˜šœ ˜šœœ˜ Jšœ˜Jšœ œ˜Jš œœœœœœ˜0Jšœ˜——Jš œ œœœœ˜.J˜Jšœ/™/Jš œ œœœ œœÏc˜CJ˜Jšœ œœœ ˜4Jš œœ œ œœ˜7Jšœœ˜Jšœœ˜J˜Jšœ œœœ ˜:Jš œœ œ œœ˜7Jšœœ˜Jšœœ˜J˜Jšœ.œ˜2Jšœœœ˜*Jšœœœ˜%J˜Jšœ™J˜Jšœ_™_JšœV™VšÏn œœœ˜7šœ˜Jšœ œ ˜J˜ Jšœ-œ˜2Jšœœ ˜—Jš œœœœœœ˜DJšœ˜J˜J˜Jš œœœœœœ˜GJ˜J˜Jšœœ˜Jšœœ˜$Jšœœ˜J˜šœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜šœ˜Jšœ œœœ˜BJš œœœœœœ˜DJ˜Jšœ1œœ˜LJ˜—Jšœ˜—Jšœ œ˜šœ˜Jšœ˜J˜J˜/J˜1J˜—J˜J™Jšœ’™’—šŸœœ˜Jšœ1œ˜7Jšœœ˜Jšœ1œœ˜LJšœœ˜J˜J˜—šŸœœœ˜Jšœœœ˜šœ œ˜.Jšœ(œœ˜OJšœ˜—Jšœœ˜ Jšœ˜J˜—šŸœœœ˜Jšœœ˜šœ œœ ˜.JšœJœ˜R—Jšœœ˜ Jšœ˜J˜—Jšœ™J˜šŸ œœœœ˜1šÏbœœ˜!Jšœ˜Jšœ œ˜Jšœœ˜Jšœ)™)Jšœ˜Jšœœ˜Jšœ˜J™Jšœ ™ —Jšœ˜Jšœ#˜#Jšœ3˜3J˜J˜J˜˜Jšœ™—Jšœœœ˜!šœ˜Jšœ™—Jšœœ˜"šœ˜ Jšœœ˜ šœœ˜ šœ œ˜Jš œœœ œœ˜4Jšœ˜Jšœ˜——Jšœ*™*—J˜Jšœ&™&Jšœ ˜šœ˜Jšœ7™7—Jšœœ œ9˜PšœU˜UJšœ4™4—šœ˜Jšœœ œ˜*Jšœ˜ Jšœ™—J˜J™šœ™J™c—šœ˜JšœA™A—Jšœ-žœœž˜TJšœž˜J˜—š Ÿœœœœœ˜9š œœž,˜Kš œœ˜Jšœ3™3š  œœ˜Jšœ8™8Jšœ/˜/šœœœ3˜BJšœœ˜!šœœ˜ Jšœœ˜ J˜>Jšœž ˜$—J˜/—J˜J˜—šœ™JšœS™S—Jšœ!˜$J˜—šœ™JšœK™K—Jšœ&˜)Jšœž˜—J™šœ$™$Jšœ@™@—šœ#˜#JšœL™L—šœ4œ˜YJšœ™—Jšœ˜Jšœž ˜%J˜—JšœS™SJšœ5™5š Ÿœœœ œœ˜9šœœ žœ˜Jšœ-™-Jšœ ˜ Jš œœ œœœ˜@J˜—Jšœ œ ˜Jšœ˜J˜—J™cš œœ˜(šœœž˜#šœœœ5˜Gšœ˜JšœK˜K—Jšœ˜—J˜—šœœ#˜.šœ˜Jšœ˜Jšœœœ˜2šœœœ˜,šœ˜šœœœ˜Jšœœœœ˜:Jšœœ!˜CJ˜(J˜—J™ šœ˜JšœJ˜J—Jšœ˜——Jšœ˜—šœ˜Jšœœ!œ˜GJšœ(˜(Jšœ"˜"Jšœ˜——J˜J˜—šŸœœœ œœœœœ˜@Jšœ4™4Jšœ*™*š œœœœœ˜!Jšœœœœœœœœ˜3—Jšœœœ˜Jšœ!˜!šœ œœœ˜š œœœ œœ˜+Jšœœœ ˜š œ œœœœœ˜LJšœ™Jšœœœœ˜š œœœœœœ˜AJšœœ˜(—Jšœœ˜"J˜—Jš˜——šœ˜J˜——šŸœœC˜YJšœœœ˜šœ˜Jšœ=˜=—Jšœ7™7Jšœ<™