Skip to content

Commit dbd687d

Browse files
committed
Cleanup and improvements regarding sparse bit
- Remove remainders from an earlier refactoring (local variable `_isDataBoundByPC` is not used) - The check for this variable in UserRuntimeZero was thus always false - However, calls to join (label groups) are already put under a conditional checking the sparse bit in the Stack2JS phase - Also `wrap_block_rhs` seems to already only occour under such a conditional generated by Stack2JS, so that the blocking label is only updated when the sparse bit is not set. The function wrap_block_rhs thus becomes redundant, as it only returns its argument. - Make code less redundant - Use consistent naming: use "sparse bit" in all places, instead of "bound slot" etc.
1 parent b7e6532 commit dbd687d

File tree

3 files changed

+106
-112
lines changed

3 files changed

+106
-112
lines changed

compiler/src/Stack2JS.hs

Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ addLibs xs = vcat (map addOneLib xs)
9292

9393
data TheState = TheState { freshCounter :: Integer
9494
, frameSize :: Int
95-
, boundSlot :: Int
96-
, consts :: Raw.Consts
97-
, stHFN :: IR.HFN }
95+
, sparseSlot :: Int
96+
, consts :: Raw.Consts
97+
, stHFN :: IR.HFN }
9898

9999
type RetKontText = PP.Doc
100100

@@ -103,7 +103,7 @@ type W = RWS Bool ([LibAccess], [Basics.AtomName], [RetKontText]) TheState
103103

104104
initState = TheState { freshCounter = 0
105105
, frameSize = error "frameSize should not be accessed yet"
106-
, boundSlot = error "boundSlot should not be accessed yet"
106+
, sparseSlot = error "sparseSlot should not be accessed yet"
107107
, consts = error "consts should not be accessed yet"
108108
, stHFN = error "stHFN should not be accessed yet"
109109
}
@@ -207,22 +207,21 @@ constsToJS consts =
207207
instance ToJS FunDef where
208208
toJS fdef@(FunDef hfn stacksize consts bb irfdef) = do
209209
{--
210-
| | | ... | <bound_slot> |
210+
| | | ... | <sparse slot> |
211211
^ ^
212212
| |
213213
SP stacksize
214214
215215
--}
216216
let _frameSize = stacksize + 1
217217

218-
modify (\s -> s { frameSize = _frameSize, boundSlot = stacksize, stHFN = hfn, consts = consts } ) -- + 1 for the _data_bound_by_pc flag; 2021-03-17; AA
219-
let lits = constsToJS consts
218+
modify (\s -> s { frameSize = _frameSize, sparseSlot = stacksize, stHFN = hfn, consts = consts } ) -- + 1 for the sparse flag; 2021-03-17; AA
219+
let lits = constsToJS consts
220220
jj <- toJS bb
221221
debug <- ask
222-
let (irdeps, libdeps, atomdeps ) = IR.ppDeps irfdef
223-
b_slot_index = text "_SP + " PP.<> (PP.int stacksize)
224-
data_bound_by_pc_slot = text "_STACK[ " PP.<> b_slot_index PP.<> "]"
225-
222+
let (irdeps, libdeps, atomdeps ) = IR.ppDeps irfdef
223+
sparseSlotIdxPP <- ppSparseSlotIdx
224+
226225
return $
227226
vcat [text "this." PP.<> ppId hfn <+> text "=" <+> ppArgs ["$env"] <+> text "=> {"
228227
, if debug then nest 2 $ text "rt.debug" <+> (PP.parens . PP.quotes. ppId) hfn
@@ -232,8 +231,11 @@ instance ToJS FunDef where
232231
"let _STACK = _T.callStack",
233232
"let _SP = _T._sp",
234233
"let _SP_OLD",
235-
data_bound_by_pc_slot <+> " = _T.checkDataBoundsEntry($env.__dataLevel)",
236-
"_T.boundSlot = " <+> b_slot_index,
234+
-- Update sparse bit at function entry:
235+
-- Check whether environment's data level, and the label and data level of R0 are bound by PC.
236+
-- Requires sparseSlot to be updated first.
237+
"_T.sparseSlot = " <+> sparseSlotIdxPP,
238+
"_T.updateSparseBitOnEntry($env.__dataLevel)",
237239
lits,
238240
jj]
239241
, text "}"
@@ -285,7 +287,7 @@ binOpToJS = \case
285287
Neq -> "rt.neq"
286288
Concat -> "+"
287289
HasField -> "rt.hasField"
288-
LatticeJoin -> "rt.join"
290+
LatticeJoin -> "rt.raw_join"
289291
-- No RT operations (should be moved to a different datatype)
290292
RaisedTo -> error "Not a runtime operation"
291293
-- Not yet implemented in IR2Raw
@@ -350,25 +352,22 @@ ir2js (MkFunClosures envBindings funBindings) = do
350352
where ppEnvIds env ls =
351353
vcat (
352354
(map (\(a,b) -> semi $ (ppId env) PP.<> text "." PP.<> (ppId a) <+> text "=" <+> ppId b ) ls)
353-
++
354-
[ppId env PP.<> text ".__dataLevel = " <+> (jsFunCall "rt.join" (map (\(_, b) -> ppId b <> text ".dataLevel") ls )) ]
355+
++
356+
[ppId env PP.<> text ".__dataLevel = " <+> jsFunCall (text $ binOpToJS Basics.LatticeJoin) (map (\(_, b) -> ppId b <> text ".dataLevel") ls ) ]
355357
)
356358
hsepc ls = semi $ PP.hsep (PP.punctuate (text ",") ls)
357359

358360

359-
ir2js (SetState c x) =
360-
let rhs = case c of MonBlock -> ppFunCall "rt.wrap_block_rhs" [ppId x]
361-
_ -> ppId x
362-
363-
in return $ semi $ monStateToJs c <+> "=" <+> rhs
361+
ir2js (SetState c x) = return $ semi $ monStateToJs c <+> "=" <+> ppId x
364362

365363
ir2js (RTAssertion a) = return $ ppRTAssertionCode jsFunCall a
366364

367-
ir2js (LabelGroup ii) = do
368-
ii' <- mapM ppLevelOp ii
369-
b_slot <- data_bounded_by_pc_slot
365+
ir2js (LabelGroup ii) = do
366+
ii' <- mapM ppLevelOp ii
367+
sparseSlot <- ppSparseSlot
370368
return $ vcat $
371-
[ "if (!" <+> b_slot <+> ") {"
369+
[ -- "if (! _T.getSparseBit()) {" -- Alternative, but involves extra call to RT
370+
"if (!" <+> sparseSlot <+> ") {"
372371
, nest 2 (vcat ii')
373372
, text "}"
374373
]
@@ -391,30 +390,33 @@ ir2js InvalidateSparseBit = return $
391390
{-- TERMINATORS --}
392391

393392

394-
tr2js (Call bb bb2) = do
395-
_frameSize <- frameSize <$> get
396-
_boundSlot <- boundSlot <$> get
397-
_consts <- consts <$> get
398-
modify (\s -> s {frameSize = 0, boundSlot = _boundSlot - _frameSize - 5})
393+
tr2js (Call bb bb2) = do
394+
_frameSize <- gets frameSize
395+
_sparseSlot <- gets sparseSlot
396+
_consts <- gets consts
397+
modify (\s -> s {frameSize = 0, sparseSlot = _sparseSlot - _frameSize - 5})
399398
-- AA; 2021-04-24; Because
400399
js <- toJS bb
401-
modify (\s -> s { frameSize = _frameSize, boundSlot = _boundSlot })
400+
modify (\s -> s { frameSize = _frameSize, sparseSlot = _sparseSlot })
402401
-- TODO: AA; 2021-04-24; we should really be using a reader monad here for frame size
403402
-- #codedebt
404403
js2 <- toJS bb2
405-
kname <- freshKontName
406-
b_slot <- data_bounded_by_pc_slot
407-
b_slot_index <- b_slot_absolute_index
408-
let jsKont =
409-
vcat ["this." PP.<> ppId kname <+> text "= () => {",
410-
nest 2 $
411-
vcat [
404+
kname <- freshKontName
405+
sparseSlotIdxPP <- ppSparseSlotIdx
406+
let jsKont =
407+
vcat ["this." PP.<> ppId kname <+> text "= () => {",
408+
nest 2 $
409+
vcat [
412410
"let _T = rt.runtime.$t",
413411
"let _STACK = _T.callStack",
414412
"let _SP = _T._sp",
413+
-- TODO Do we need this? It seems to be only used zero or one time in the generated places.
414+
-- So we could instead just use the let where it is actually set.
415415
"let _SP_OLD",
416-
b_slot <+> "= _T.checkDataBounds(" <+> b_slot <+> ")" ,
417-
"_T.boundSlot =" <+> b_slot_index ,
416+
-- Check data bound at return point (could have received labelled information or raised).
417+
-- Requires sparseSlot to be updated first.
418+
"_T.sparseSlot =" <+> sparseSlotIdxPP,
419+
"_T.updateSparseBitOnReturn()",
418420
constsToJS _consts , -- 2021-05-18; TODO: optimize by including only the _used_ constants
419421
js2
420422
],
@@ -479,15 +481,16 @@ monStateToJs c =
479481
R0_TLev -> text "r0_tlev"
480482

481483

482-
data_bounded_by_pc_slot :: W PP.Doc
483-
data_bounded_by_pc_slot = do
484-
_b <- boundSlot <$> get
485-
return $ text "_STACK[ _SP + " PP.<> (text (show (_b))) PP.<> text "]"
484+
ppSparseSlotIdx :: W PP.Doc
485+
ppSparseSlotIdx = do
486+
s <- gets sparseSlot
487+
return $ text "_SP + " PP.<+> PP.int s
488+
489+
ppSparseSlot :: W PP.Doc
490+
ppSparseSlot = do
491+
idx <- ppSparseSlotIdx
492+
return $ text "_STACK[ " PP.<> idx PP.<> text "]"
486493

487-
b_slot_absolute_index :: W PP.Doc
488-
b_slot_absolute_index = do
489-
_b <- boundSlot<$> get
490-
return $ text "_SP +" PP.<+> (PP.int _b)
491494
-----------------------------------------------------------
492495

493496

@@ -563,16 +566,16 @@ jsFunCall a b = semi $ ppFunCall a b
563566

564567

565568
freshEnvVar :: W VarName
566-
freshEnvVar = do
567-
k <- freshCounter <$> get
568-
modify (\s -> s { freshCounter = k + 1 } )
569+
freshEnvVar = do
570+
k <- gets freshCounter
571+
modify (\s -> s { freshCounter = k + 1 } )
569572
return $ VN $ "$$$env" ++ (show k)
570573

571-
574+
572575
freshKontName :: W VarName
573-
freshKontName = do
574-
j <- freshCounter <$> get
575-
HFN s <- stHFN <$> get
576+
freshKontName = do
577+
j <- gets freshCounter
578+
HFN s <- gets stHFN
576579
modify (\s -> s { freshCounter = j + 1})
577580
return $ VN $ "$$$" ++ s ++ "$$$kont" ++ (show j)
578581

rt/src/Thread.mts

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,7 @@ export class Thread {
212212
next : () => any;
213213
callStack : any []
214214
_sp : number;
215-
boundSlot : number;
216-
217-
_isDataBoundByPC: boolean = false;
215+
sparseSlot : number; // slot on the stack holding the sparse bit (whether data is bounded by PC)
218216

219217
processDebuggingName: string;
220218

@@ -250,7 +248,7 @@ export class Thread {
250248
---> stack growth direction --->
251249
252250
|---------+-------------------+--------------+-----------------------------+---------------+-------------------------+--------------------|
253-
| sp_prev | pc at return site | ret callback | mclear at the time of entry | branching bit | [escaping locals] | bound_slot |
251+
| sp_prev | pc at return site | ret callback | mclear at the time of entry | branching bit | [escaping locals] | sparse slot |
254252
|---------+-------------------+--------------+-----------------------------+---------------+-------------------------+--------------------|
255253
| sp - 5 | sp - 4 | sp - 3 | sp - 2 | sp - 1 | sp ... (sp + framesize) | sp + framesize + 1 |
256254
@@ -329,7 +327,7 @@ export class Thread {
329327

330328
showStack () {
331329
console.log ("======== SHOW STACK ========= ")
332-
console.log (`sp = ${this._sp} boundslot = ${this.boundSlot}`)
330+
console.log (`sp = ${this._sp} sparseSlot = ${this.sparseSlot}`)
333331
let j = this._sp - 1
334332
let stack = this.callStack
335333
while ( j > 0) {
@@ -365,46 +363,51 @@ export class Thread {
365363
return f;
366364
}
367365

366+
getSparseBit() {
367+
return this.callStack[this.sparseSlot]
368+
}
368369

369-
invalidateSparseBit () {
370-
this.callStack[this.boundSlot] = false;
371-
}
372-
373-
// Check whether the label of R0 (argument), the data level of R0 and the given one are bound by PC.
374-
checkDataBoundsEntry (x: Level) {
375-
const _pc = this.pc
376-
let y =
377-
flowsTo(this.r0_lev, _pc)
378-
&&
379-
flowsTo (x, _pc)
380-
&& (this.r0_val._troupeType == undefined
381-
? true
382-
: flowsTo (this.r0_val.dataLevel, _pc)
383-
)
384-
385-
386-
// this._isDataBoundByPC = y;
387-
return y;
388-
}
389-
390-
// Check whether the label of R0 (return value) and the data level of R0 are bound by PC.
391-
// Return false if x is false.
392-
// TODO Better check x directly and do not call this function if false (now that _isDataBoundByPC is not updated).
393-
checkDataBounds (x: boolean) {
394-
const _pc = this.pc
395-
let y =
396-
x? flowsTo(this.r0_lev, _pc)
397-
&& (this.r0_val._troupeType == undefined
398-
? true
399-
: flowsTo (this.r0_val.dataLevel, _pc)
400-
)
401-
: false
402-
403-
// this._isDataBoundByPC = y;
404-
return y;
370+
invalidateSparseBit() {
371+
this.callStack[this.sparseSlot] = false;
405372
}
406373

407-
374+
private setSparseBit(b: boolean) {
375+
this.callStack[this.sparseSlot] = b;
376+
}
377+
378+
/**
379+
* Check whether the label of R0 (argument), the data level of R0 and the given label are bound by PC
380+
* and update sparse bit accordingly.
381+
*/
382+
updateSparseBitOnEntry(x: Level) {
383+
const _pc = this.pc
384+
// Only non-basic types (_troupeType is defined) have a data level. For those, it
385+
// is sufficient to check r0_val.dataLevel ⊑ _pc, as r0_lev ⊑ r0_val.dataLev. For
386+
// base types, we instead have to check r0_lev ⊑ _pc.
387+
if (this.r0_val._troupeType != undefined) {
388+
this.setSparseBit(flowsTo(x, _pc) && flowsTo(this.r0_val.dataLevel, _pc))
389+
} else {
390+
this.setSparseBit(flowsTo(x, _pc) && flowsTo(this.r0_lev, _pc))
391+
}
392+
}
393+
394+
/**
395+
* If the sparse bit is set, check whether it is still valid for the returned value (and reset it if not):
396+
* Check whether the label of R0 (return value) and the data level of R0 are bound by PC.
397+
*/
398+
updateSparseBitOnReturn() {
399+
const _pc = this.pc
400+
if (this.getSparseBit()) {
401+
// Only non-basic types (_troupeType is defined) have a data level. For those, it
402+
// is sufficient to check r0_val.dataLevel ⊑ _pc, as r0_lev ⊑ r0_val.dataLev. For
403+
// base types, we instead have to check r0_lev ⊑ _pc.
404+
if (this.r0_val._troupeType != undefined) {
405+
this.setSparseBit(flowsTo(this.r0_val.dataLevel, _pc))
406+
} else {
407+
this.setSparseBit(flowsTo(this.r0_lev, _pc))
408+
}
409+
}
410+
}
408411

409412

410413
runNext (theFun, args, nm) {
@@ -619,8 +622,7 @@ export class Thread {
619622

620623

621624
blockdeclto (auth, bl_to = this.pc) {
622-
let is_bounded_by_pc = flowsTo (this.pc, bl_to);
623-
if (!is_bounded_by_pc) {
625+
if (! flowsTo (this.pc, bl_to)) {
624626
this.threadError ("The provided target blocking level is lower than the current pc\n" +
625627
` | the current pc: ${this.pc.stringRep()}\n` +
626628
` | target blocking level: ${bl_to.stringRep()}`)

rt/src/builtins/UserRuntimeZero.mts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,22 +92,11 @@ export class UserRuntimeZero {
9292
this.runtime.ret (x)
9393
}
9494

95-
join (...xs) {
96-
if (this.runtime.$t._isDataBoundByPC) {
97-
return this.runtime.$t.pc
98-
}
95+
// SimpleRT
96+
raw_join(...xs) : Level {
9997
return lub.apply (null, xs)
10098
}
10199

102-
wrap_block_rhs (x) {
103-
if (this.runtime.$t._isDataBoundByPC) {
104-
return this.runtime.$t.bl
105-
} else {
106-
return x;
107-
}
108-
109-
}
110-
111100
// SpecialRT
112101
raw_invalidateSparseBit() {
113102
this.runtime.$t.invalidateSparseBit()

0 commit comments

Comments
 (0)