linearscan-hoopl 0.7.0 → 0.8.0
raw patch · 7 files changed
+305/−211 lines, 7 filesdep −fuzzcheckdep ~linearscandep ~linearscan-hoopl
Dependencies removed: fuzzcheck
Dependency ranges changed: linearscan, linearscan-hoopl
Files
- LinearScan/Hoopl.hs +0/−2
- linearscan-hoopl.cabal +5/−5
- test/AsmTest.hs +6/−5
- test/Assembly.hs +4/−8
- test/Generated.hs +24/−23
- test/Main.hs +190/−168
- test/Programs/Ordered.hs +76/−0
LinearScan/Hoopl.hs view
@@ -35,7 +35,6 @@ setRegisters :: [(Int, PhysReg)] -> nv e x -> Env (nr e x) mkMoveOps :: PhysReg -> VarId -> PhysReg -> Env [nr O O]- mkSwapOps :: PhysReg -> VarId -> PhysReg -> VarId -> Env [nr O O] mkSaveOps :: PhysReg -> VarId -> Env [nr O O] mkRestoreOps :: VarId -> PhysReg -> Env [nr O O] @@ -88,7 +87,6 @@ NodeOC n -> getReferences n , moveOp = \x xv y -> fmap NodeOO <$> mkMoveOps x xv y- , swapOp = \x xv y yv -> fmap NodeOO <$> mkSwapOps x xv y yv , saveOp = \x xv -> fmap NodeOO <$> mkSaveOps x xv , restoreOp = \yv y -> fmap NodeOO <$> mkRestoreOps yv y
linearscan-hoopl.cabal view
@@ -1,5 +1,5 @@ name: linearscan-hoopl-version: 0.7.0+version: 0.8.0 synopsis: Makes it easy to use the linearscan register allocator with Hoopl homepage: http://github.com/jwiegley/linearscan-hoopl license: BSD3@@ -27,7 +27,7 @@ build-depends: base >=4.7 && <5 , hoopl >= 3.10.0.1- , linearscan >= 0.7.0.0+ , linearscan >= 0.8.0 , containers , transformers , free@@ -46,17 +46,17 @@ Generated Programs.Blocked Programs.Exhaustion+ Programs.Ordered build-depends: base >=3 , linearscan , hspec >= 1.4.4 , hspec-expectations >= 0.3 , hoopl >= 3.10.0.1 && < 3.11- , linearscan >= 0.7.0.0- , linearscan-hoopl >= 0.7.0.0+ , linearscan >= 0.8.0+ , linearscan-hoopl >= 0.8.0 , containers >= 0.5.5 , transformers >= 0.3.0.0 , lens-family-core , deepseq , QuickCheck- , fuzzcheck
test/AsmTest.hs view
@@ -12,10 +12,11 @@ import Normal () import Test.Hspec -asmTestLiteral :: Int -> Program (Node IRVar) -> Maybe String -> Expectation-asmTestLiteral regs program mexpected = do+asmTestLiteral :: UseVerifier -> Int -> Program (Node IRVar) -> Maybe String+ -> Expectation+asmTestLiteral verif regs program mexpected = do let (graph, entry) = runSimpleUniqueMonad $ compile "entry" program- case allocateHoopl regs 0 8 VerifyEnabled entry graph of+ case allocateHoopl regs 0 8 verif entry graph of Left (dump, err) -> error $ "Allocation failed: " ++ intercalate "\n" err ++ "\n" ++ dump@@ -35,7 +36,7 @@ -> Program (Node (Assign VarId PhysReg)) -> Expectation asmTest regs program- = asmTestLiteral regs program+ = asmTestLiteral VerifyEnabledStrict regs program . Just . showGraph show . fst@@ -43,4 +44,4 @@ . compile "entry" asmTest_ :: Int -> Program (Node IRVar) -> Expectation-asmTest_ regs program = asmTestLiteral regs program Nothing+asmTest_ regs program = asmTestLiteral VerifyEnabledStrict regs program Nothing
test/Assembly.hs view
@@ -14,7 +14,7 @@ import Control.Applicative import Data.Foldable as F import qualified Data.List-import Data.Maybe (fromMaybe, maybeToList)+import Data.Maybe (fromMaybe) import Data.Monoid import Data.Traversable import Lens.Family hiding (Constant)@@ -80,6 +80,8 @@ instance (Show v, VarReg v) => Show (Node v e x) where show (Label l) = " label \"" ++ show l ++ "\" $ do"+ show (Alloc (Just x1) x2) =+ " alloc (Just " ++ show x1 ++ ") " ++ show x2 show (Alloc x1 x2) = " alloc " ++ show x1 ++ " " ++ show x2 show (Reclaim v) = " reclaim " ++ show v show (Instr i) = " " ++ show i@@ -210,7 +212,7 @@ where go :: Node IRVar e x -> [VarInfo] go (Label _) = mempty- go (Alloc msrc dst) = F.concatMap (mkv Input) (maybeToList msrc) <>+ go (Alloc msrc dst) = maybe mempty (mkv Input) msrc <> mkv Output dst <> [ vinfo Output (Left n) | n <- [2..16] ] go (Reclaim src) = mkv Input src@@ -252,12 +254,6 @@ mkMoveOps sreg svar dreg = do return [Move (Assign svar sreg) (Assign svar dreg)]-- mkSwapOps sreg svar dreg dvar = do- save1 <- mkSaveOps svar sreg- save2 <- mkMoveOps dreg dvar sreg- rest <- mkRestoreOps svar dreg- return $ save1 ++ save2 ++ rest mkSaveOps sreg dvar = do off <- getStackSlot (Just dvar)
test/Generated.hs view
@@ -13,11 +13,11 @@ import qualified Data.IntMap as IM import qualified Data.IntSet as IS import Data.List (intercalate, isInfixOf)-import LinearScan (UseVerifier(VerifyDisabled))+import LinearScan (UseVerifier(..)) import LinearScan.Hoopl-import Test.FuzzCheck as F hiding (branch) import Test.Hspec import Test.QuickCheck hiding (label)+import Test.QuickCheck.Test (isSuccess) import Unsafe.Coerce instance Arbitrary IRVar where@@ -93,24 +93,25 @@ show = showGraph show generatedTests :: SpecWith ()-generatedTests =- it "Handles generated tests" $- (\a -> fuzzCheck' a 100 (return ())) $ do- (graph :: Graph (Node IRVar) C C) <- "create graph" ?> pure <$> rand- let GMany _ body _ = graph- let entry = unsafeCoerce- (head (IS.elems (unsafeCoerce- (externalEntryLabels body))))- case allocateHoopl 32 0 8 VerifyDisabled entry graph of- Left (dump, err)- | any ("Cannot insert interval onto unhandled list"- `isInfixOf`) err ->- True `shouldBe` True- | otherwise ->- error $ "Allocation failed: " ++ intercalate "\n" err ++ "\n"- ++ dump- Right graph' -> do- let g = showGraph show graph'- _ <- evaluate (length g)- return ()- -- putStrLn $ "---- Compiled ----\n" ++ g+generatedTests = it "Handles generated tests" $ do+ res <- quickCheckWithResult stdArgs { maxSuccess = 1000000000 } $+ forAll arbitrary testGraph+ isSuccess res `shouldBe` True++testGraph :: Graph (Node IRVar) C C -> Expectation+testGraph graph@(GMany _ body _) =+ case IS.elems (unsafeCoerce (externalEntryLabels body)) of+ [] -> True `shouldBe` True+ (entry:_) -> case allocateHoopl 32 0 8 VerifyEnabled+ (unsafeCoerce entry) graph of+ Left (dump, err)+ | any ("Cannot insert onto unhandled:" `isInfixOf`) err ->+ True `shouldBe` True+ | otherwise ->+ error $ "Allocation failed: "+ ++ intercalate "\n" err ++ "\n" ++ dump+ Right graph' -> do+ let g = showGraph show graph'+ _ <- evaluate (length g)+ return ()+ -- putStrLn $ "---- Compiled ----\n" ++ g
test/Main.hs view
@@ -6,9 +6,24 @@ import AsmTest import Assembly import Generated+import LinearScan (UseVerifier(..)) import LinearScan.Hoopl.DSL -- import Programs.Blocked import Programs.Exhaustion+import Programs.Ordered+import Programs.Overcommit+import Programs.Incoming+import Programs.Residency+import Programs.Residency2+import Programs.BranchAlloc+import Programs.Returned+import Programs.Restoration+import Programs.Allocation+import Programs.Allocation2+import Programs.Allocation3+import Programs.UponEntry+import Programs.Overlapped+import Programs.ReturnAssign import Test.Hspec -- | The objective of these tests is to present a program to the register@@ -23,9 +38,24 @@ describe "Call tests" callTests describe "Loop tests" loopTests - describe "Program tests" $ do+ describe "Edge-case tests" $ do+ let runTest k = asmTestLiteral VerifyEnabled 32 k Nothing it "Near exhaustion program" $ asmTest_ 32 exhaustion1- -- it "Blocked register program" $ asmTest_ 32 regBlocked+ -- it "Blocked register program" $ runTest regBlocked+ it "Orders reservations" $ runTest regOrdered+ it "Guards against over-committing" $ runTest overCommitted+ it "Properly reserves incoming registers" $ runTest regsIncoming+ it "Handles edge-case 1 residency scenario" $ runTest residency+ it "Handles edge-case 2 residency scenario" $ runTest residency2+ it "A case of residency involving branches" $ runTest branchAlloc+ it "Frees registers properly before returning" $ runTest freeBeforeReturn+ it "Restoration after a graph edge split" $ runTest restoration+ it "Handles edge-case 1 allocation scenario" $ runTest allocation+ it "Handles edge-case 2 allocation scenario" $ runTest allocation2+ it "Handles edge-case 3 allocation scenario" $ runTest allocation3+ it "Allocates correctly on block entry" $ runTest uponEntry+ it "Register over-allocation edge-case" $ runTest overlapped+ it "Does not assign after a return_" $ runTest returnAssign describe "Generated tests" generatedTests @@ -114,14 +144,13 @@ lc (r5 v13) add (r0 v0) (r1 v1) (r0 v2) add (r2 v3) (r3 v4) (r1 v5)- restore 0 (r3 v7)- add (r4 v6) (r3 v7) (r2 v8)- save (r0 v2) 32- restore 8 (r4 v9)- restore 16 (r0 v10)- add (r4 v9) (r0 v10) (r3 v11)+ restore 0 (r2 v7)+ add (r4 v6) (r2 v7) (r2 v8)+ restore 16 (r4 v10)+ restore 8 (r3 v9)+ add (r3 v9) (r4 v10) (r3 v11) restore 24 (r4 v12)- add (r4 v12) (r5 v13) (r0 v14)+ add (r4 v12) (r5 v13) (r4 v14) return_ it "Single long-lived variable" $ asmTest 32@@ -421,13 +450,9 @@ add (r10 v15) (r11 v16) (r29 v17) add (r12 v18) (r13 v19) (r30 v20) add (r14 v21) (r15 v22) (r31 v23)- save (r31 v23) 0 -- jww (2015-05-26): These saves are unnecessary add (r16 v24) (r17 v25) (r31 v26)- save (r31 v26) 8 add (r18 v27) (r19 v28) (r31 v29)- save (r31 v29) 16 add (r20 v30) (r21 v31) (r31 v32)- save (r31 v32) 24 add (r22 v33) (r23 v34) (r31 v35) add (r0 v0) (r1 v1) (r24 v2) add (r2 v3) (r3 v4) (r25 v5)@@ -436,13 +461,9 @@ add (r8 v12) (r9 v13) (r28 v14) add (r10 v15) (r11 v16) (r29 v17) add (r12 v18) (r13 v19) (r30 v20)- restore 0 (r0 v23) add (r14 v21) (r15 v22) (r0 v23)- restore 8 (r1 v26) add (r16 v24) (r17 v25) (r1 v26)- restore 16 (r2 v29) add (r18 v27) (r19 v28) (r2 v29)- restore 24 (r3 v32) add (r20 v30) (r21 v31) (r3 v32) add (r22 v33) (r23 v34) (r31 v35) return_@@ -636,25 +657,25 @@ add v2 v5 v6 add v2 v6 v7 add v2 v7 v8- return_) $+ return_) $ do - do label "entry" $ do- lc (r0 v0)- lc (r1 v1)- add (r0 v0) (r1 v1) (r0 v2)- jump "L2"+ label "entry" $ do+ lc (r0 v0)+ lc (r1 v1)+ add (r0 v0) (r1 v1) (r0 v2)+ jump "L2" - label "L2" $ do- lc (r1 v3)- add (r0 v2) (r1 v3) (r1 v4)- add (r0 v2) (r1 v4) (r1 v5)- jump "L3"+ label "L2" $ do+ lc (r1 v3)+ add (r0 v2) (r1 v3) (r1 v4)+ add (r0 v2) (r1 v4) (r1 v5)+ jump "L3" - label "L3" $ do- add (r0 v2) (r1 v5) (r1 v6)- add (r0 v2) (r1 v6) (r1 v7)- add (r0 v2) (r1 v7) (r0 v8)- return_+ label "L3" $ do+ add (r0 v2) (r1 v5) (r1 v6)+ add (r0 v2) (r1 v6) (r1 v7)+ add (r0 v2) (r1 v7) (r0 v8)+ return_ it "Inserts resolving moves" $ asmTest 3 (do label "entry" $ do@@ -677,34 +698,35 @@ label "B4" $ do add v3 v3 v0- return_) $+ return_) $ do - do label "entry" $ do- lc (r0 v0)- lc (r1 v1)- add (r0 v0) (r1 v1) (r2 v2)- branch (r2 v2) "B3" "B2"+ label "entry" $ do+ lc (r0 v0)+ lc (r1 v1)+ add (r0 v0) (r1 v1) (r2 v2)+ branch (r2 v2) "B3" "B2" - label "B2" $ do- save (r0 v0) 0- add (r1 v1) (r2 v2) (r0 v3)- restore 0 (r1 v0)- add (r1 v0) (r0 v3) (r2 v4)- save (r0 v3) 8- add (r1 v0) (r2 v4) (r0 v5)- add (r2 v4) (r0 v5) (r0 v6)- restore 8 (r2 v3)- add (r0 v6) (r2 v3) (r0 v7)- jump "B4"+ label "B2" $ do+ save (r0 v0) 0+ add (r1 v1) (r2 v2) (r0 v3)+ restore 0 (r2 v0)+ add (r2 v0) (r0 v3) (r1 v4)+ save (r0 v3) 8+ add (r2 v0) (r1 v4) (r0 v5)+ add (r1 v4) (r0 v5) (r0 v6)+ restore 8 (r1 v3)+ add (r0 v6) (r1 v3) (r0 v7)+ jump "B4" - label "B3" $ do- move (r2 v2) (r0 v2)- add (r1 v1) (r0 v2) (r2 v3)- jump "B4"+ label "B3" $ do+ move (r2 v2) (r0 v2)+ move (r1 v1) (r2 v1)+ add (r2 v1) (r0 v2) (r1 v3)+ jump "B4" - label "B4" $ do- add (r2 v3) (r2 v3) (r0 v0)- return_+ label "B4" $ do+ add (r1 v3) (r1 v3) (r0 v0)+ return_ it "When resolving moves are not needed" $ asmTest 4 (do label "entry" $ do@@ -727,29 +749,29 @@ label "B4" $ do add v3 v3 v0- return_) $+ return_) $ do - do label "entry" $ do- lc (r0 v0)- lc (r1 v1)- add (r0 v0) (r1 v1) (r2 v2)- branch (r2 v2) "B2" "B3"+ label "entry" $ do+ lc (r0 v0)+ lc (r1 v1)+ add (r0 v0) (r1 v1) (r2 v2)+ branch (r2 v2) "B2" "B3" - label "B2" $ do- add (r1 v1) (r2 v2) (r3 v3)- add (r0 v0) (r0 v0) (r1 v4)- add (r0 v0) (r0 v0) (r2 v5)- add (r0 v0) (r1 v4) (r1 v6)- add (r0 v0) (r2 v5) (r1 v6)- jump "B4"+ label "B2" $ do+ add (r1 v1) (r2 v2) (r3 v3)+ add (r0 v0) (r0 v0) (r1 v4)+ add (r0 v0) (r0 v0) (r2 v5)+ add (r0 v0) (r1 v4) (r1 v6)+ add (r0 v0) (r2 v5) (r1 v6)+ jump "B4" - label "B3" $ do- add (r1 v1) (r2 v2) (r3 v3)- jump "B4"+ label "B3" $ do+ add (r1 v1) (r2 v2) (r3 v3)+ jump "B4" - label "B4" $ do- add (r3 v3) (r3 v3) (r0 v0)- return_+ label "B4" $ do+ add (r3 v3) (r3 v3) (r0 v0)+ return_ it "Another resolution case" $ asmTest 4 (do label "entry" $ do@@ -807,11 +829,11 @@ move (r0 v5) (r1 v4) save (r3 v21) 32 lc (r3 v19)- save (r3 v19) 48 save (r2 v18) 40- restore 0 (r2 v20)- move (r2 v20) (r3 v17)- save (r2 v20) 0+ save (r3 v19) 48+ restore 0 (r3 v20)+ move (r3 v20) (r2 v17)+ save (r3 v20) 0 restore 24 (r2 v15) jump "L6" @@ -819,9 +841,9 @@ branch (r1 v4) "L5" "L2" label "L5" $ do+ restore 0 (r3 v20) -- jww (2015-05-26): should be unnecessary save (r2 v15) 24- restore 0 (r2 v20) -- jww (2015-05-26): should be unnecessary- save (r2 v20) 0+ save (r3 v20) 0 restore 24 (r2 v15) restore 8 (r0 v3) jump "L3"@@ -844,27 +866,27 @@ return_) $ label "entry" $ do- lc (r3 v0)- lc (r2 v1)- add (r3 v0) (r2 v1) (r3 v2)- add (r3 v2) (r2 v1) (r1 v3)- add (r1 v3) (r3 v2) (r0 v4)- save (r3 v2) 0- add (r0 v4) (r1 v3) (r3 v5)- save (r2 v1) 32- save (r1 v3) 24- save (r0 v4) 16- save (r3 v5) 8+ lc (r0 v0)+ lc (r3 v1)+ add (r0 v0) (r3 v1) (r2 v2)+ add (r2 v2) (r3 v1) (r1 v3)+ save (r3 v1) 0+ add (r1 v3) (r2 v2) (r3 v4)+ save (r2 v2) 8+ add (r3 v4) (r1 v3) (r2 v5)+ save (r1 v3) 16+ save (r3 v4) 24+ save (r2 v5) 32 call 1000- restore 0 (r1 v2)- restore 24 (r2 v3)- add (r1 v2) (r2 v3) (r0 v6)- restore 16 (r2 v4)- restore 8 (r3 v5)- add (r2 v4) (r3 v5) (r1 v7)- add (r0 v6) (r1 v7) (r0 v8)- restore 32 (r1 v1)- add (r0 v8) (r1 v1) (r0 v0)+ restore 16 (r2 v3)+ restore 8 (r1 v2)+ add (r1 v2) (r2 v3) (r1 v6)+ restore 32 (r3 v5)+ restore 24 (r2 v4)+ add (r2 v4) (r3 v5) (r2 v7)+ add (r1 v6) (r2 v7) (r1 v8)+ restore 0 (r2 v1)+ add (r1 v8) (r2 v1) (r0 v0) return_ it "Allocates between call instructions" $ asmTest 32@@ -918,85 +940,85 @@ label "entry" $ do lc (r31 v2)- lc (r30 v3)- lc (r29 v12)- lc (r28 v16)- lc (r27 v17)- lc (r26 v35)- lc (r25 v42)- lc (r24 v45)- lc (r23 v51)- lc (r22 v53)- lc (r21 v90)- lc (r20 v100)- save (r31 v2) 88- save (r30 v3) 80- save (r29 v12) 72- save (r28 v16) 64- save (r27 v17) 56- save (r26 v35) 48- save (r25 v42) 40- save (r24 v45) 32- save (r23 v51) 24- save (r22 v53) 16- save (r21 v90) 8- save (r20 v100) 0+ save (r31 v2) 0+ lc (r31 v3)+ save (r31 v3) 8+ lc (r31 v12)+ save (r31 v12) 16+ lc (r31 v16)+ save (r31 v16) 24+ lc (r31 v17)+ save (r31 v17) 32+ lc (r31 v35)+ save (r31 v35) 40+ lc (r31 v42)+ save (r31 v42) 48+ lc (r31 v45)+ save (r31 v45) 56+ lc (r31 v51)+ save (r31 v51) 64+ lc (r31 v53)+ save (r31 v53) 72+ lc (r31 v90)+ save (r31 v90) 80+ lc (r31 v100)+ save (r31 v100) 88 call 97- restore 32 (r30 v45)- restore 24 (r29 v51)- add (r29 v51) (r30 v45) (r31 v98)- save (r31 v98) 96- restore 72 (r30 v12)- move (r30 v12) (r31 v64)- save (r31 v64) 104- restore 40 (r31 v42)- branch (r31 v42) "L92" "L62"+ restore 56 (r0 v45)+ restore 64 (r1 v51)+ add (r1 v51) (r0 v45) (r31 v98)+ restore 16 (r0 v12)+ move (r0 v12) (r0 v64)+ restore 48 (r1 v42)+ branch (r1 v42) "L2" "L3" - label "L62" $ do- lc (r31 v50)- jump "L12"+ label "L2" $ do+ save (r31 v98) 104+ lc (r31 v95)+ restore 80 (r1 v90)+ save (r31 v95) 112+ move (r1 v90) (r31 v43)+ save (r31 v43) 120+ call 95+ call 64+ restore 72 (r1 v53)+ move (r1 v53) (r1 v58)+ restore 24 (r2 v16)+ restore 104 (r3 v98)+ add (r3 v98) (r2 v16) (r0 v100)+ restore 8 (r3 v3)+ restore 40 (r2 v35)+ add (r2 v35) (r3 v3) (r2 v67)+ restore 0 (r3 v2)+ move (r3 v2) (r3 v24)+ lc (r4 v13)+ restore 32 (r5 v17)+ move (r5 v17) (r5 v32)+ return_ - label "L12" $ do+ label "L3" $ do+ lc (r0 v50)+ jump "L4"++ label "L4" $ do nop- restore 0 (r30 v100)- move (r30 v100) (r31 v44)+ restore 88 (r0 v100)+ move (r0 v100) (r31 v44)+ save (r31 v44) 96 call 30 call 32- jump "L15"+ jump "L5" - label "L15" $ do+ label "L5" $ do lc (r31 v3)+ save (r31 v3) 8 call 35- save (r31 v3) 80- lc (r31 v73)- return_-- label "L92" $ do- lc (r31 v95)- save (r31 v95) 112- restore 8 (r30 v90)- move (r30 v90) (r31 v43)- call 95- save (r31 v43) 120- call 64- restore 16 (r1 v53)- move (r1 v53) (r0 v58)- restore 64 (r1 v16)- restore 96 (r2 v98)- add (r2 v98) (r1 v16) (r1 v100)- restore 80 (r4 v3)- restore 48 (r3 v35)- add (r3 v35) (r4 v3) (r2 v67)- restore 88 (r4 v2)- move (r4 v2) (r3 v24)- lc (r4 v13)- restore 56 (r6 v17)- move (r6 v17) (r5 v32)+ lc (r1 v73) return_ loopTests :: SpecWith () loopTests = do- it "Correctly orders loop blocks" $ asmTestLiteral 4+ it "Correctly orders loop blocks" $ asmTestLiteral VerifyEnabledStrict 4 (do label "entry" $ do trace "B0" jump "B1"
+ test/Programs/Ordered.hs view
@@ -0,0 +1,76 @@+module Programs.Ordered where++import Assembly+import LinearScan.Hoopl.DSL++regOrdered :: Program (Node IRVar)+regOrdered = do+ label "entry" $ do+ copy v99 v94+ lc v6+ move v96 v73+ copy v5 v67+ add v48 v96 pr7+ move v29 v23+ offp v95 v28 v51+ move v36 v96+ move v22 v32+ call 100+ jump "L72"++ label "L72" $ do+ add v67 v22 v13+ move v0 v30+ copy v56 v39+ branch v90 "L14" "L86"++ label "L86" $ do+ return_++ label "L14" $ do+ call 37+ move v31 v34+ nop+ add v10 v22 v60+ move v6 v54+ branch v89 "L97" "L18"++ label "L18" $ do+ offlpi v3+ jump "L83"++ label "L83" $ do+ move v37 v62+ branch v10 "L47" "L33"++ label "L33" $ do+ lc v74+ return_++ label "L47" $ do+ lc v80+ jump "L68"++ label "L68" $ do+ jump "L91"++ label "L91" $ do+ lc v38+ call 45+ move v50 v16+ lc v72+ add v3 pr9 v34+ move v52 v16+ copy v76 v73+ offlpi v92+ add v6 v54 v48+ add v27 v68 v49+ return_++ label "L97" $ do+ move v95 v30+ offp v77 v31 v75+ move v95 v88+ move v67 v69+ call 50+ return_