Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions analysis/reanalyze/DEADCODE_REFACTOR_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,14 @@ Each task should:
- [x] ~~Use the `DceConfig.t` already created, thread it through DCE analysis functions~~
- [x] ~~Replace all DCE code's `!Common.Cli.debug`, `runConfig.transitive`, etc. reads with `config.debug`, `config.run.transitive`~~
- [x] ~~Make all config parameters required (not optional) - no `config option` anywhere~~
- [ ] **Thread config through Exception and Arnold analyses** - they currently call `DceConfig.current()` at each use site
- [ ] **Single entry point**: Only `Reanalyze.runAnalysisAndReport` should call `DceConfig.current()` once, then pass explicit config everywhere
- [x] Thread config through Exception and Arnold analyses (no `DceConfig.current()` in analysis code)
- [x] Single entry point: only the CLI/entry wrappers (`runAnalysisAndReport`, `DceCommand`) call `DceConfig.current()` once, then pass explicit config everywhere

**Status**: DCE code complete ✅. Exception/Arnold still need threading.
**Status**: Complete ✅ (DCE + Exception + Arnold).

**Test**: Create two configs with different settings, run analysis with each - should respect the config, not read globals.

**Estimated effort**: Medium (DCE done; Exception/Arnold similar effort)
**Estimated effort**: Medium (done)

### Task 3: Make `ProcessDeadAnnotations` state explicit (P3)

Expand Down Expand Up @@ -262,13 +262,13 @@ Each task should:
**Value**: Enforce purity - no hidden global reads.

**Changes**:
- [ ] Verify `DceConfig.current()` only called in `Reanalyze.runAnalysisAndReport` (entry point)
- [ ] Verify no calls to `DceConfig.current()` in `Dead*.ml`, `Exception.ml`, `Arnold.ml` analysis code
- [ ] All analysis functions take explicit `~config` parameter
- [x] Verify `DceConfig.current()` only called in entry wrappers (CLI / `runAnalysisAndReport`)
- [x] Verify no calls to `DceConfig.current()` in `Dead*.ml`, `Exception.ml`, `Arnold.ml` analysis code
- [x] All analysis functions take explicit `~config` parameter

**Test**: `grep -r "DceConfig.current" analysis/reanalyze/src/{Dead,Exception,Arnold}.ml` returns zero results.
**Test**: `grep -r "DceConfig.current" analysis/reanalyze/src/{Dead,Exception,Arnold}.ml` returns zero results.

**Estimated effort**: Trivial (verification only, assuming Task 2 complete)
**Estimated effort**: Trivial (done)

### Task 11: Integration and order-independence verification

Expand Down
123 changes: 66 additions & 57 deletions analysis/reanalyze/src/Arnold.ml
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ module Stats = struct

let logLoop () = incr nInfiniteLoops

let logCache ~functionCall ~hit ~loc =
let logCache ~config ~functionCall ~hit ~loc =
incr nCacheChecks;
if hit then incr nCacheHits;
if !Common.Cli.debug then
Log_.warning ~config:(DceConfig.current ()) ~forStats:false ~loc
if config.DceConfig.cli.debug then
Log_.warning ~config ~forStats:false ~loc
(Termination
{
termination = TerminationAnalysisInternal;
Expand All @@ -123,9 +123,9 @@ module Stats = struct
(FunctionCall.toString functionCall);
})

let logResult ~functionCall ~loc ~resString =
if !Common.Cli.debug then
Log_.warning ~config:(DceConfig.current ()) ~forStats:false ~loc
let logResult ~config ~functionCall ~loc ~resString =
if config.DceConfig.cli.debug then
Log_.warning ~config ~forStats:false ~loc
(Termination
{
termination = TerminationAnalysisInternal;
Expand Down Expand Up @@ -591,7 +591,8 @@ module ExtendFunctionTable = struct
if args |> List.for_all checkArg then Some (path, loc) else None
| _ -> None

let traverseExpr ~functionTable ~progressFunctions ~valueBindingsTable =
let traverseExpr ~config ~functionTable ~progressFunctions ~valueBindingsTable
=
let super = Tast_mapper.default in
let expr (self : Tast_mapper.mapper) (e : Typedtree.expression) =
(match e.exp_desc with
Expand All @@ -609,8 +610,8 @@ module ExtendFunctionTable = struct
if not (callee |> FunctionTable.isInFunctionInTable ~functionTable)
then (
functionTable |> FunctionTable.addFunction ~functionName;
if !Common.Cli.debug then
Log_.warning ~config:(DceConfig.current ()) ~forStats:false ~loc
if config.DceConfig.cli.debug then
Log_.warning ~config ~forStats:false ~loc
(Termination
{
termination = TerminationAnalysisInternal;
Expand All @@ -631,9 +632,8 @@ module ExtendFunctionTable = struct
->
functionTable
|> FunctionTable.addLabelToKind ~functionName ~label;
if !Common.Cli.debug then
Log_.warning ~config:(DceConfig.current ()) ~forStats:false
~loc
if config.DceConfig.cli.debug then
Log_.warning ~config ~forStats:false ~loc
(Termination
{
termination = TerminationAnalysisInternal;
Expand All @@ -649,16 +649,16 @@ module ExtendFunctionTable = struct
in
{super with Tast_mapper.expr}

let run ~functionTable ~progressFunctions ~valueBindingsTable
let run ~config ~functionTable ~progressFunctions ~valueBindingsTable
(expression : Typedtree.expression) =
let traverseExpr =
traverseExpr ~functionTable ~progressFunctions ~valueBindingsTable
traverseExpr ~config ~functionTable ~progressFunctions ~valueBindingsTable
in
expression |> traverseExpr.expr traverseExpr |> ignore
end

module CheckExpressionWellFormed = struct
let traverseExpr ~functionTable ~valueBindingsTable =
let traverseExpr ~config ~functionTable ~valueBindingsTable =
let super = Tast_mapper.default in
let checkIdent ~path ~loc =
if path |> FunctionTable.isInFunctionInTable ~functionTable then
Expand Down Expand Up @@ -699,9 +699,8 @@ module CheckExpressionWellFormed = struct
|> FunctionTable.addFunction ~functionName;
functionTable
|> FunctionTable.addLabelToKind ~functionName ~label;
if !Common.Cli.debug then
Log_.warning ~config:(DceConfig.current ())
~forStats:false ~loc:body.exp_loc
if config.DceConfig.cli.debug then
Log_.warning ~config ~forStats:false ~loc:body.exp_loc
(Termination
{
termination = TerminationAnalysisInternal;
Expand All @@ -719,22 +718,27 @@ module CheckExpressionWellFormed = struct
in
{super with Tast_mapper.expr}

let run ~functionTable ~valueBindingsTable (expression : Typedtree.expression)
=
let traverseExpr = traverseExpr ~functionTable ~valueBindingsTable in
let run ~config ~functionTable ~valueBindingsTable
(expression : Typedtree.expression) =
let traverseExpr =
traverseExpr ~config ~functionTable ~valueBindingsTable
in
expression |> traverseExpr.expr traverseExpr |> ignore
end

module Compile = struct
type ctx = {
config: DceConfig.t;
currentFunctionName: FunctionName.t;
functionTable: FunctionTable.t;
innerRecursiveFunctions: (FunctionName.t, FunctionName.t) Hashtbl.t;
isProgressFunction: Path.t -> bool;
}

let rec expression ~ctx (expr : Typedtree.expression) =
let {currentFunctionName; functionTable; isProgressFunction} = ctx in
let {config; currentFunctionName; functionTable; isProgressFunction} =
ctx
in
let loc = expr.exp_loc in
let notImplemented case =
Log_.error ~loc
Expand Down Expand Up @@ -874,8 +878,8 @@ module Compile = struct
Hashtbl.replace ctx.innerRecursiveFunctions oldFunctionName
newFunctionName;
newFunctionDefinition.body <- Some (vb_expr |> expression ~ctx:newCtx);
if !Common.Cli.debug then
Log_.warning ~config:(DceConfig.current ()) ~forStats:false ~loc:pat_loc
if config.DceConfig.cli.debug then
Log_.warning ~config ~forStats:false ~loc:pat_loc
(Termination
{
termination = TerminationAnalysisInternal;
Expand Down Expand Up @@ -1069,8 +1073,9 @@ module Eval = struct
let lookupCache ~functionCall (cache : cache) =
Hashtbl.find_opt cache functionCall

let updateCache ~functionCall ~loc ~state (cache : cache) =
Stats.logResult ~functionCall ~resString:(state |> State.toString) ~loc;
let updateCache ~config ~functionCall ~loc ~state (cache : cache) =
Stats.logResult ~config ~functionCall ~resString:(state |> State.toString)
~loc;
if not (Hashtbl.mem cache functionCall) then
Hashtbl.replace cache functionCall state

Expand Down Expand Up @@ -1101,7 +1106,7 @@ module Eval = struct
true)
else false

let rec runFunctionCall ~cache ~callStack ~functionArgs ~functionTable
let rec runFunctionCall ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~loc ~state functionCallToInstantiate : State.t =
let pos = loc.Location.loc_start in
let functionCall =
Expand All @@ -1113,7 +1118,7 @@ module Eval = struct
let stateAfterCall =
match cache |> lookupCache ~functionCall with
| Some stateAfterCall ->
Stats.logCache ~functionCall ~hit:true ~loc;
Stats.logCache ~config ~functionCall ~hit:true ~loc;
{
stateAfterCall with
trace = Trace.Tcall (call, stateAfterCall.progress);
Expand All @@ -1126,7 +1131,7 @@ module Eval = struct
~loc ~state
then {state with trace = Trace.Tcall (call, state.progress)}
else (
Stats.logCache ~functionCall ~hit:false ~loc;
Stats.logCache ~config ~functionCall ~hit:false ~loc;
let functionDefinition =
functionTable |> FunctionTable.getFunctionDefinition ~functionName
in
Expand All @@ -1138,23 +1143,24 @@ module Eval = struct
in
let stateAfterCall =
body
|> run ~cache ~callStack ~functionArgs:functionCall.functionArgs
~functionTable ~madeProgressOn ~state:(State.init ())
|> run ~config ~cache ~callStack
~functionArgs:functionCall.functionArgs ~functionTable
~madeProgressOn ~state:(State.init ())
in
cache |> updateCache ~functionCall ~loc ~state:stateAfterCall;
cache |> updateCache ~config ~functionCall ~loc ~state:stateAfterCall;
(* Invariant: run should restore the callStack *)
callStack |> CallStack.removeFunctionCall ~functionCall;
let trace = Trace.Tcall (call, stateAfterCall.progress) in
{stateAfterCall with trace})
in
State.seq state stateAfterCall

and run ~(cache : cache) ~callStack ~functionArgs ~functionTable
and run ~config ~(cache : cache) ~callStack ~functionArgs ~functionTable
~madeProgressOn ~state (command : Command.t) : State.t =
match command with
| Call (FunctionCall functionCall, loc) ->
functionCall
|> runFunctionCall ~cache ~callStack ~functionArgs ~functionTable
|> runFunctionCall ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~loc ~state
| Call ((ProgressFunction _ as call), _pos) ->
let state1 =
Expand All @@ -1179,7 +1185,7 @@ module Eval = struct
| c :: nextCommands ->
let state1 =
c
|> run ~cache ~callStack ~functionArgs ~functionTable
|> run ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~state
in
let madeProgressOn, callStack =
Expand All @@ -1202,7 +1208,7 @@ module Eval = struct
commands
|> List.map (fun c ->
c
|> run ~cache ~callStack ~functionArgs ~functionTable
|> run ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~state:stateNoTrace)
in
State.seq state (states |> State.unorderedSequence)
Expand All @@ -1213,36 +1219,36 @@ module Eval = struct
commands
|> List.map (fun c ->
c
|> run ~cache ~callStack ~functionArgs ~functionTable
|> run ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~state:stateNoTrace)
in
State.seq state (states |> State.nondet)
| SwitchOption {functionCall; loc; some; none} -> (
let stateAfterCall =
functionCall
|> runFunctionCall ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~loc ~state
|> runFunctionCall ~config ~cache ~callStack ~functionArgs
~functionTable ~madeProgressOn ~loc ~state
in
match stateAfterCall.valuesOpt with
| None ->
Command.nondet [some; none]
|> run ~cache ~callStack ~functionArgs ~functionTable ~madeProgressOn
~state:stateAfterCall
|> run ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~state:stateAfterCall
| Some values ->
let runOpt c progressOpt =
match progressOpt with
| None -> State.init ~progress:Progress ()
| Some progress ->
c
|> run ~cache ~callStack ~functionArgs ~functionTable
|> run ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn ~state:(State.init ~progress ())
in
let stateNone = values |> Values.getNone |> runOpt none in
let stateSome = values |> Values.getSome |> runOpt some in
State.seq stateAfterCall (State.nondet [stateSome; stateNone]))

let analyzeFunction ~cache ~functionTable ~loc functionName =
if !Common.Cli.debug then
let analyzeFunction ~config ~cache ~functionTable ~loc functionName =
if config.DceConfig.cli.debug then
Log_.log "@[<v 2>@,@{<warning>Termination Analysis@} for @{<info>%s@}@]@."
functionName;
let pos = loc.Location.loc_start in
Expand All @@ -1263,10 +1269,10 @@ module Eval = struct
in
let state =
body
|> run ~cache ~callStack ~functionArgs ~functionTable
|> run ~config ~cache ~callStack ~functionArgs ~functionTable
~madeProgressOn:FunctionCallSet.empty ~state:(State.init ())
in
cache |> updateCache ~functionCall ~loc ~state
cache |> updateCache ~config ~functionCall ~loc ~state
end

let progressFunctionsFromAttributes attributes =
Expand All @@ -1285,7 +1291,7 @@ let progressFunctionsFromAttributes attributes =
| _ -> [])
else None

let traverseAst ~valueBindingsTable =
let traverseAst ~config ~valueBindingsTable =
let super = Tast_mapper.default in
let value_bindings (self : Tast_mapper.mapper) (recFlag, valueBindings) =
(* Update the table of value bindings for variables *)
Expand Down Expand Up @@ -1352,12 +1358,13 @@ let traverseAst ~valueBindingsTable =
recursiveDefinitions
|> List.iter (fun (_, body) ->
body
|> ExtendFunctionTable.run ~functionTable ~progressFunctions
~valueBindingsTable);
|> ExtendFunctionTable.run ~config ~functionTable
~progressFunctions ~valueBindingsTable);
recursiveDefinitions
|> List.iter (fun (_, body) ->
body
|> CheckExpressionWellFormed.run ~functionTable ~valueBindingsTable);
|> CheckExpressionWellFormed.run ~config ~functionTable
~valueBindingsTable);
functionTable
|> Hashtbl.iter
(fun
Expand All @@ -1376,17 +1383,19 @@ let traverseAst ~valueBindingsTable =
|> Compile.expression
~ctx:
{
config;
currentFunctionName = functionName;
functionTable;
innerRecursiveFunctions = Hashtbl.create 1;
isProgressFunction;
}))
~functionName);
if !Common.Cli.debug then FunctionTable.dump functionTable;
if config.DceConfig.cli.debug then FunctionTable.dump functionTable;
let cache = Eval.createCache () in
functionsToAnalyze
|> List.iter (fun (functionName, loc) ->
functionName |> Eval.analyzeFunction ~cache ~functionTable ~loc);
functionName
|> Eval.analyzeFunction ~config ~cache ~functionTable ~loc);
Stats.newRecursiveFunctions ~numFunctions:(Hashtbl.length functionTable));
valueBindings
|> List.iter (fun valueBinding ->
Expand All @@ -1395,16 +1404,16 @@ let traverseAst ~valueBindingsTable =
in
{super with Tast_mapper.value_bindings}

let processStructure (structure : Typedtree.structure) =
let processStructure ~config (structure : Typedtree.structure) =
Stats.newFile ();
let valueBindingsTable = Hashtbl.create 1 in
let traverseAst = traverseAst ~valueBindingsTable in
let traverseAst = traverseAst ~config ~valueBindingsTable in
structure |> traverseAst.structure traverseAst |> ignore

let processCmt (cmt_infos : Cmt_format.cmt_infos) =
let processCmt ~config (cmt_infos : Cmt_format.cmt_infos) =
match cmt_infos.cmt_annots with
| Interface _ -> ()
| Implementation structure -> processStructure structure
| Implementation structure -> processStructure ~config structure
| _ -> ()

let reportStats () = Stats.dump ~ppf:Format.std_formatter
let reportStats ~config:_ = Stats.dump ~ppf:Format.std_formatter
Loading
Loading