Merge remote-tracking branch 'ocaml/trunk' into flambda_prereq-init_assign
commit
029e1bbbbc
427
.depend
427
.depend
|
@ -1,27 +1,36 @@
|
|||
utils/ccomp.cmi :
|
||||
utils/clflags.cmi :
|
||||
utils/clflags.cmi : utils/misc.cmi
|
||||
utils/config.cmi :
|
||||
utils/consistbl.cmi :
|
||||
utils/misc.cmi : utils/clflags.cmi
|
||||
utils/identifiable.cmi :
|
||||
utils/misc.cmi :
|
||||
utils/numbers.cmi : utils/identifiable.cmi
|
||||
utils/tbl.cmi :
|
||||
utils/terminfo.cmi :
|
||||
utils/timings.cmi :
|
||||
utils/warnings.cmi :
|
||||
utils/ccomp.cmo : utils/misc.cmi utils/config.cmi utils/clflags.cmi \
|
||||
utils/ccomp.cmi
|
||||
utils/ccomp.cmx : utils/misc.cmx utils/config.cmx utils/clflags.cmx \
|
||||
utils/ccomp.cmi
|
||||
utils/clflags.cmo : utils/config.cmi utils/clflags.cmi
|
||||
utils/clflags.cmx : utils/config.cmx utils/clflags.cmi
|
||||
utils/clflags.cmo : utils/misc.cmi utils/config.cmi utils/clflags.cmi
|
||||
utils/clflags.cmx : utils/misc.cmx utils/config.cmx utils/clflags.cmi
|
||||
utils/config.cmo : utils/config.cmi
|
||||
utils/config.cmx : utils/config.cmi
|
||||
utils/consistbl.cmo : utils/consistbl.cmi
|
||||
utils/consistbl.cmx : utils/consistbl.cmi
|
||||
utils/misc.cmo : utils/clflags.cmi utils/misc.cmi
|
||||
utils/misc.cmx : utils/clflags.cmx utils/misc.cmi
|
||||
utils/identifiable.cmo : utils/misc.cmi utils/identifiable.cmi
|
||||
utils/identifiable.cmx : utils/misc.cmx utils/identifiable.cmi
|
||||
utils/misc.cmo : utils/misc.cmi
|
||||
utils/misc.cmx : utils/misc.cmi
|
||||
utils/numbers.cmo : utils/identifiable.cmi utils/numbers.cmi
|
||||
utils/numbers.cmx : utils/identifiable.cmx utils/numbers.cmi
|
||||
utils/tbl.cmo : utils/tbl.cmi
|
||||
utils/tbl.cmx : utils/tbl.cmi
|
||||
utils/terminfo.cmo : utils/terminfo.cmi
|
||||
utils/terminfo.cmx : utils/terminfo.cmi
|
||||
utils/timings.cmo : utils/timings.cmi
|
||||
utils/timings.cmx : utils/timings.cmi
|
||||
utils/warnings.cmo : utils/warnings.cmi
|
||||
utils/warnings.cmx : utils/warnings.cmi
|
||||
parsing/ast_helper.cmi : parsing/parsetree.cmi parsing/longident.cmi \
|
||||
|
@ -265,17 +274,17 @@ typing/oprint.cmo : typing/outcometree.cmi parsing/asttypes.cmi \
|
|||
typing/oprint.cmx : typing/outcometree.cmi parsing/asttypes.cmi \
|
||||
typing/oprint.cmi
|
||||
typing/parmatch.cmo : utils/warnings.cmi typing/untypeast.cmi \
|
||||
typing/types.cmi typing/typedtree.cmi typing/subst.cmi typing/predef.cmi \
|
||||
typing/path.cmi parsing/parsetree.cmi utils/misc.cmi \
|
||||
parsing/longident.cmi parsing/location.cmi typing/ident.cmi \
|
||||
typing/env.cmi typing/ctype.cmi typing/btype.cmi parsing/asttypes.cmi \
|
||||
parsing/ast_helper.cmi typing/parmatch.cmi
|
||||
typing/types.cmi typing/typedtreeIter.cmi typing/typedtree.cmi \
|
||||
typing/subst.cmi typing/predef.cmi typing/path.cmi parsing/parsetree.cmi \
|
||||
utils/misc.cmi parsing/longident.cmi parsing/location.cmi \
|
||||
typing/ident.cmi typing/env.cmi typing/ctype.cmi typing/btype.cmi \
|
||||
parsing/asttypes.cmi parsing/ast_helper.cmi typing/parmatch.cmi
|
||||
typing/parmatch.cmx : utils/warnings.cmx typing/untypeast.cmx \
|
||||
typing/types.cmx typing/typedtree.cmx typing/subst.cmx typing/predef.cmx \
|
||||
typing/path.cmx parsing/parsetree.cmi utils/misc.cmx \
|
||||
parsing/longident.cmx parsing/location.cmx typing/ident.cmx \
|
||||
typing/env.cmx typing/ctype.cmx typing/btype.cmx parsing/asttypes.cmi \
|
||||
parsing/ast_helper.cmx typing/parmatch.cmi
|
||||
typing/types.cmx typing/typedtreeIter.cmx typing/typedtree.cmx \
|
||||
typing/subst.cmx typing/predef.cmx typing/path.cmx parsing/parsetree.cmi \
|
||||
utils/misc.cmx parsing/longident.cmx parsing/location.cmx \
|
||||
typing/ident.cmx typing/env.cmx typing/ctype.cmx typing/btype.cmx \
|
||||
parsing/asttypes.cmi parsing/ast_helper.cmx typing/parmatch.cmi
|
||||
typing/path.cmo : typing/ident.cmi typing/path.cmi
|
||||
typing/path.cmx : typing/ident.cmx typing/path.cmi
|
||||
typing/predef.cmo : typing/types.cmi typing/path.cmi parsing/parsetree.cmi \
|
||||
|
@ -446,6 +455,7 @@ bytecomp/bytelink.cmi : bytecomp/symtable.cmi bytecomp/cmo_format.cmi
|
|||
bytecomp/bytepackager.cmi : typing/ident.cmi typing/env.cmi
|
||||
bytecomp/bytesections.cmi :
|
||||
bytecomp/cmo_format.cmi : bytecomp/lambda.cmi typing/ident.cmi
|
||||
bytecomp/debuginfo.cmi : parsing/location.cmi bytecomp/lambda.cmi
|
||||
bytecomp/dll.cmi :
|
||||
bytecomp/emitcode.cmi : bytecomp/instruct.cmi bytecomp/cmo_format.cmi
|
||||
bytecomp/instruct.cmi : typing/types.cmi typing/subst.cmi \
|
||||
|
@ -514,6 +524,10 @@ bytecomp/bytepackager.cmx : typing/typemod.cmx bytecomp/translmod.cmx \
|
|||
bytecomp/bytegen.cmx bytecomp/bytepackager.cmi
|
||||
bytecomp/bytesections.cmo : utils/config.cmi bytecomp/bytesections.cmi
|
||||
bytecomp/bytesections.cmx : utils/config.cmx bytecomp/bytesections.cmi
|
||||
bytecomp/debuginfo.cmo : parsing/location.cmi bytecomp/lambda.cmi \
|
||||
bytecomp/debuginfo.cmi
|
||||
bytecomp/debuginfo.cmx : parsing/location.cmx bytecomp/lambda.cmx \
|
||||
bytecomp/debuginfo.cmi
|
||||
bytecomp/dll.cmo : utils/misc.cmi utils/config.cmi bytecomp/dll.cmi
|
||||
bytecomp/dll.cmx : utils/misc.cmx utils/config.cmx bytecomp/dll.cmi
|
||||
bytecomp/emitcode.cmo : bytecomp/translmod.cmi typing/primitive.cmi \
|
||||
|
@ -622,16 +636,18 @@ bytecomp/translcore.cmx : typing/types.cmx bytecomp/typeopt.cmx \
|
|||
typing/btype.cmx parsing/asttypes.cmi bytecomp/translcore.cmi
|
||||
bytecomp/translmod.cmo : typing/types.cmi typing/typedtree.cmi \
|
||||
bytecomp/translobj.cmi bytecomp/translcore.cmi bytecomp/translclass.cmi \
|
||||
typing/printtyp.cmi typing/primitive.cmi typing/predef.cmi \
|
||||
typing/path.cmi typing/mtype.cmi utils/misc.cmi parsing/longident.cmi \
|
||||
parsing/location.cmi bytecomp/lambda.cmi typing/ident.cmi typing/env.cmi \
|
||||
typing/ctype.cmi parsing/asttypes.cmi bytecomp/translmod.cmi
|
||||
bytecomp/translattribute.cmi typing/printtyp.cmi typing/primitive.cmi \
|
||||
typing/predef.cmi typing/path.cmi typing/mtype.cmi utils/misc.cmi \
|
||||
parsing/longident.cmi parsing/location.cmi bytecomp/lambda.cmi \
|
||||
typing/ident.cmi typing/env.cmi typing/ctype.cmi utils/clflags.cmi \
|
||||
parsing/asttypes.cmi bytecomp/translmod.cmi
|
||||
bytecomp/translmod.cmx : typing/types.cmx typing/typedtree.cmx \
|
||||
bytecomp/translobj.cmx bytecomp/translcore.cmx bytecomp/translclass.cmx \
|
||||
typing/printtyp.cmx typing/primitive.cmx typing/predef.cmx \
|
||||
typing/path.cmx typing/mtype.cmx utils/misc.cmx parsing/longident.cmx \
|
||||
parsing/location.cmx bytecomp/lambda.cmx typing/ident.cmx typing/env.cmx \
|
||||
typing/ctype.cmx parsing/asttypes.cmi bytecomp/translmod.cmi
|
||||
bytecomp/translattribute.cmx typing/printtyp.cmx typing/primitive.cmx \
|
||||
typing/predef.cmx typing/path.cmx typing/mtype.cmx utils/misc.cmx \
|
||||
parsing/longident.cmx parsing/location.cmx bytecomp/lambda.cmx \
|
||||
typing/ident.cmx typing/env.cmx typing/ctype.cmx utils/clflags.cmx \
|
||||
parsing/asttypes.cmi bytecomp/translmod.cmi
|
||||
bytecomp/translobj.cmo : typing/primitive.cmi utils/misc.cmi \
|
||||
parsing/longident.cmi bytecomp/lambda.cmi typing/ident.cmi typing/env.cmi \
|
||||
utils/clflags.cmi typing/btype.cmi parsing/asttypes.cmi \
|
||||
|
@ -642,38 +658,44 @@ bytecomp/translobj.cmx : typing/primitive.cmx utils/misc.cmx \
|
|||
bytecomp/translobj.cmi
|
||||
bytecomp/typeopt.cmo : typing/types.cmi typing/typedtree.cmi \
|
||||
typing/predef.cmi typing/path.cmi bytecomp/lambda.cmi typing/ident.cmi \
|
||||
typing/env.cmi typing/ctype.cmi bytecomp/typeopt.cmi
|
||||
typing/env.cmi typing/ctype.cmi typing/btype.cmi bytecomp/typeopt.cmi
|
||||
bytecomp/typeopt.cmx : typing/types.cmx typing/typedtree.cmx \
|
||||
typing/predef.cmx typing/path.cmx bytecomp/lambda.cmx typing/ident.cmx \
|
||||
typing/env.cmx typing/ctype.cmx bytecomp/typeopt.cmi
|
||||
typing/env.cmx typing/ctype.cmx typing/btype.cmx bytecomp/typeopt.cmi
|
||||
asmcomp/CSEgen.cmi : asmcomp/mach.cmi
|
||||
asmcomp/asmgen.cmi : bytecomp/lambda.cmi asmcomp/cmm.cmi
|
||||
asmcomp/asmgen.cmi : utils/timings.cmi bytecomp/lambda.cmi asmcomp/cmm.cmi
|
||||
asmcomp/asmlibrarian.cmi :
|
||||
asmcomp/asmlink.cmi : asmcomp/cmx_format.cmi
|
||||
asmcomp/asmpackager.cmi : typing/env.cmi
|
||||
asmcomp/branch_relaxation.cmi : asmcomp/linearize.cmi \
|
||||
asmcomp/branch_relaxation_intf.cmo
|
||||
asmcomp/build_export_info.cmi : asmcomp/export_info.cmi
|
||||
asmcomp/clambda.cmi : bytecomp/lambda.cmi typing/ident.cmi \
|
||||
asmcomp/debuginfo.cmi parsing/asttypes.cmi
|
||||
bytecomp/debuginfo.cmi parsing/asttypes.cmi
|
||||
asmcomp/closure.cmi : bytecomp/lambda.cmi asmcomp/clambda.cmi
|
||||
asmcomp/cmm.cmi : bytecomp/lambda.cmi typing/ident.cmi asmcomp/debuginfo.cmi
|
||||
asmcomp/closure_offsets.cmi :
|
||||
asmcomp/cmm.cmi : bytecomp/lambda.cmi typing/ident.cmi \
|
||||
bytecomp/debuginfo.cmi
|
||||
asmcomp/cmmgen.cmi : asmcomp/cmx_format.cmi asmcomp/cmm.cmi \
|
||||
asmcomp/clambda.cmi
|
||||
asmcomp/cmx_format.cmi : asmcomp/clambda.cmi
|
||||
asmcomp/coloring.cmi :
|
||||
asmcomp/comballoc.cmi : asmcomp/mach.cmi
|
||||
asmcomp/compilenv.cmi : typing/ident.cmi asmcomp/cmx_format.cmi \
|
||||
asmcomp/clambda.cmi
|
||||
asmcomp/compilenv.cmi : utils/timings.cmi typing/ident.cmi \
|
||||
asmcomp/cmx_format.cmi asmcomp/clambda.cmi
|
||||
asmcomp/deadcode.cmi : asmcomp/mach.cmi
|
||||
asmcomp/debuginfo.cmi : parsing/location.cmi bytecomp/lambda.cmi
|
||||
asmcomp/emit.cmi : asmcomp/linearize.cmi asmcomp/cmm.cmi
|
||||
asmcomp/emitaux.cmi : asmcomp/debuginfo.cmi
|
||||
asmcomp/emitaux.cmi : bytecomp/debuginfo.cmi
|
||||
asmcomp/export_info.cmi : typing/ident.cmi
|
||||
asmcomp/export_info_for_pack.cmi : asmcomp/export_info.cmi
|
||||
asmcomp/flambda_to_clambda.cmi : asmcomp/export_info.cmi asmcomp/clambda.cmi
|
||||
asmcomp/import_approx.cmi :
|
||||
asmcomp/interf.cmi : asmcomp/mach.cmi
|
||||
asmcomp/linearize.cmi : asmcomp/reg.cmi asmcomp/mach.cmi bytecomp/lambda.cmi \
|
||||
asmcomp/debuginfo.cmi
|
||||
bytecomp/debuginfo.cmi
|
||||
asmcomp/liveness.cmi : asmcomp/mach.cmi
|
||||
asmcomp/mach.cmi : asmcomp/reg.cmi bytecomp/lambda.cmi asmcomp/debuginfo.cmi \
|
||||
asmcomp/cmm.cmi asmcomp/arch.cmo
|
||||
asmcomp/mach.cmi : asmcomp/reg.cmi bytecomp/lambda.cmi \
|
||||
bytecomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/arch.cmo
|
||||
asmcomp/printclambda.cmi : asmcomp/clambda.cmi
|
||||
asmcomp/printcmm.cmi : asmcomp/cmm.cmi
|
||||
asmcomp/printlinear.cmi : asmcomp/linearize.cmi
|
||||
|
@ -685,11 +707,12 @@ asmcomp/reloadgen.cmi : asmcomp/reg.cmi asmcomp/mach.cmi
|
|||
asmcomp/schedgen.cmi : asmcomp/mach.cmi asmcomp/linearize.cmi
|
||||
asmcomp/scheduling.cmi : asmcomp/linearize.cmi
|
||||
asmcomp/selectgen.cmi : utils/tbl.cmi asmcomp/reg.cmi asmcomp/mach.cmi \
|
||||
typing/ident.cmi asmcomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/arch.cmo
|
||||
typing/ident.cmi bytecomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/arch.cmo
|
||||
asmcomp/selection.cmi : asmcomp/mach.cmi asmcomp/cmm.cmi
|
||||
asmcomp/spill.cmi : asmcomp/mach.cmi
|
||||
asmcomp/split.cmi : asmcomp/mach.cmi
|
||||
asmcomp/strmatch.cmi : asmcomp/cmm.cmi
|
||||
asmcomp/un_anf.cmi : asmcomp/clambda.cmi
|
||||
asmcomp/x86_ast.cmi :
|
||||
asmcomp/x86_dsl.cmi : asmcomp/x86_ast.cmi
|
||||
asmcomp/x86_gas.cmi : asmcomp/x86_ast.cmi
|
||||
|
@ -703,26 +726,28 @@ asmcomp/CSEgen.cmx : asmcomp/reg.cmx asmcomp/proc.cmx asmcomp/mach.cmx \
|
|||
asmcomp/cmm.cmx asmcomp/CSEgen.cmi
|
||||
asmcomp/arch.cmo : utils/clflags.cmi
|
||||
asmcomp/arch.cmx : utils/clflags.cmx
|
||||
asmcomp/asmgen.cmo : bytecomp/translmod.cmi asmcomp/split.cmi \
|
||||
asmcomp/spill.cmi asmcomp/selection.cmi asmcomp/scheduling.cmi \
|
||||
asmcomp/reload.cmi asmcomp/reg.cmi asmcomp/proc.cmi asmcomp/printmach.cmi \
|
||||
asmcomp/printlinear.cmi asmcomp/printcmm.cmi asmcomp/printclambda.cmi \
|
||||
typing/primitive.cmi utils/misc.cmi asmcomp/mach.cmi parsing/location.cmi \
|
||||
asmcomp/liveness.cmi asmcomp/linearize.cmi asmcomp/interf.cmi \
|
||||
asmcomp/emitaux.cmi asmcomp/emit.cmi asmcomp/deadcode.cmi \
|
||||
utils/config.cmi asmcomp/compilenv.cmi asmcomp/comballoc.cmi \
|
||||
asmcomp/coloring.cmi asmcomp/cmmgen.cmi asmcomp/cmm.cmi \
|
||||
asmcomp/closure.cmi utils/clflags.cmi asmcomp/CSE.cmo asmcomp/asmgen.cmi
|
||||
asmcomp/asmgen.cmx : bytecomp/translmod.cmx asmcomp/split.cmx \
|
||||
asmcomp/spill.cmx asmcomp/selection.cmx asmcomp/scheduling.cmx \
|
||||
asmcomp/reload.cmx asmcomp/reg.cmx asmcomp/proc.cmx asmcomp/printmach.cmx \
|
||||
asmcomp/printlinear.cmx asmcomp/printcmm.cmx asmcomp/printclambda.cmx \
|
||||
typing/primitive.cmx utils/misc.cmx asmcomp/mach.cmx parsing/location.cmx \
|
||||
asmcomp/liveness.cmx asmcomp/linearize.cmx asmcomp/interf.cmx \
|
||||
asmcomp/emitaux.cmx asmcomp/emit.cmx asmcomp/deadcode.cmx \
|
||||
utils/config.cmx asmcomp/compilenv.cmx asmcomp/comballoc.cmx \
|
||||
asmcomp/coloring.cmx asmcomp/cmmgen.cmx asmcomp/cmm.cmx \
|
||||
asmcomp/closure.cmx utils/clflags.cmx asmcomp/CSE.cmx asmcomp/asmgen.cmi
|
||||
asmcomp/asmgen.cmo : bytecomp/translmod.cmi utils/timings.cmi \
|
||||
asmcomp/split.cmi asmcomp/spill.cmi asmcomp/selection.cmi \
|
||||
asmcomp/scheduling.cmi asmcomp/reload.cmi asmcomp/reg.cmi \
|
||||
asmcomp/proc.cmi asmcomp/printmach.cmi asmcomp/printlinear.cmi \
|
||||
asmcomp/printcmm.cmi asmcomp/printclambda.cmi typing/primitive.cmi \
|
||||
utils/misc.cmi asmcomp/mach.cmi parsing/location.cmi asmcomp/liveness.cmi \
|
||||
asmcomp/linearize.cmi asmcomp/interf.cmi asmcomp/emitaux.cmi \
|
||||
asmcomp/emit.cmi asmcomp/deadcode.cmi utils/config.cmi \
|
||||
asmcomp/compilenv.cmi asmcomp/comballoc.cmi asmcomp/coloring.cmi \
|
||||
asmcomp/cmmgen.cmi asmcomp/cmm.cmi asmcomp/closure.cmi utils/clflags.cmi \
|
||||
asmcomp/CSE.cmo asmcomp/asmgen.cmi
|
||||
asmcomp/asmgen.cmx : bytecomp/translmod.cmx utils/timings.cmx \
|
||||
asmcomp/split.cmx asmcomp/spill.cmx asmcomp/selection.cmx \
|
||||
asmcomp/scheduling.cmx asmcomp/reload.cmx asmcomp/reg.cmx \
|
||||
asmcomp/proc.cmx asmcomp/printmach.cmx asmcomp/printlinear.cmx \
|
||||
asmcomp/printcmm.cmx asmcomp/printclambda.cmx typing/primitive.cmx \
|
||||
utils/misc.cmx asmcomp/mach.cmx parsing/location.cmx asmcomp/liveness.cmx \
|
||||
asmcomp/linearize.cmx asmcomp/interf.cmx asmcomp/emitaux.cmx \
|
||||
asmcomp/emit.cmx asmcomp/deadcode.cmx utils/config.cmx \
|
||||
asmcomp/compilenv.cmx asmcomp/comballoc.cmx asmcomp/coloring.cmx \
|
||||
asmcomp/cmmgen.cmx asmcomp/cmm.cmx asmcomp/closure.cmx utils/clflags.cmx \
|
||||
asmcomp/CSE.cmx asmcomp/asmgen.cmi
|
||||
asmcomp/asmlibrarian.cmo : utils/misc.cmi parsing/location.cmi \
|
||||
utils/config.cmi asmcomp/compilenv.cmi asmcomp/cmx_format.cmi \
|
||||
utils/clflags.cmi asmcomp/clambda.cmi utils/ccomp.cmi asmcomp/asmlink.cmi \
|
||||
|
@ -731,26 +756,26 @@ asmcomp/asmlibrarian.cmx : utils/misc.cmx parsing/location.cmx \
|
|||
utils/config.cmx asmcomp/compilenv.cmx asmcomp/cmx_format.cmi \
|
||||
utils/clflags.cmx asmcomp/clambda.cmx utils/ccomp.cmx asmcomp/asmlink.cmx \
|
||||
asmcomp/asmlibrarian.cmi
|
||||
asmcomp/asmlink.cmo : bytecomp/runtimedef.cmi utils/misc.cmi \
|
||||
parsing/location.cmi asmcomp/emitaux.cmi asmcomp/emit.cmi \
|
||||
asmcomp/asmlink.cmo : utils/timings.cmi bytecomp/runtimedef.cmi \
|
||||
utils/misc.cmi parsing/location.cmi asmcomp/emitaux.cmi asmcomp/emit.cmi \
|
||||
utils/consistbl.cmi utils/config.cmi asmcomp/compilenv.cmi \
|
||||
asmcomp/cmx_format.cmi asmcomp/cmmgen.cmi utils/clflags.cmi \
|
||||
utils/ccomp.cmi asmcomp/asmgen.cmi asmcomp/asmlink.cmi
|
||||
asmcomp/asmlink.cmx : bytecomp/runtimedef.cmx utils/misc.cmx \
|
||||
parsing/location.cmx asmcomp/emitaux.cmx asmcomp/emit.cmx \
|
||||
asmcomp/asmlink.cmx : utils/timings.cmx bytecomp/runtimedef.cmx \
|
||||
utils/misc.cmx parsing/location.cmx asmcomp/emitaux.cmx asmcomp/emit.cmx \
|
||||
utils/consistbl.cmx utils/config.cmx asmcomp/compilenv.cmx \
|
||||
asmcomp/cmx_format.cmi asmcomp/cmmgen.cmx utils/clflags.cmx \
|
||||
utils/ccomp.cmx asmcomp/asmgen.cmx asmcomp/asmlink.cmi
|
||||
asmcomp/asmpackager.cmo : typing/typemod.cmi bytecomp/translmod.cmi \
|
||||
utils/misc.cmi parsing/location.cmi typing/ident.cmi typing/env.cmi \
|
||||
utils/config.cmi asmcomp/compilenv.cmi asmcomp/cmx_format.cmi \
|
||||
utils/clflags.cmi utils/ccomp.cmi asmcomp/asmlink.cmi asmcomp/asmgen.cmi \
|
||||
asmcomp/asmpackager.cmi
|
||||
utils/timings.cmi utils/misc.cmi parsing/location.cmi typing/ident.cmi \
|
||||
typing/env.cmi utils/config.cmi asmcomp/compilenv.cmi \
|
||||
asmcomp/cmx_format.cmi utils/clflags.cmi utils/ccomp.cmi \
|
||||
asmcomp/asmlink.cmi asmcomp/asmgen.cmi asmcomp/asmpackager.cmi
|
||||
asmcomp/asmpackager.cmx : typing/typemod.cmx bytecomp/translmod.cmx \
|
||||
utils/misc.cmx parsing/location.cmx typing/ident.cmx typing/env.cmx \
|
||||
utils/config.cmx asmcomp/compilenv.cmx asmcomp/cmx_format.cmi \
|
||||
utils/clflags.cmx utils/ccomp.cmx asmcomp/asmlink.cmx asmcomp/asmgen.cmx \
|
||||
asmcomp/asmpackager.cmi
|
||||
utils/timings.cmx utils/misc.cmx parsing/location.cmx typing/ident.cmx \
|
||||
typing/env.cmx utils/config.cmx asmcomp/compilenv.cmx \
|
||||
asmcomp/cmx_format.cmi utils/clflags.cmx utils/ccomp.cmx \
|
||||
asmcomp/asmlink.cmx asmcomp/asmgen.cmx asmcomp/asmpackager.cmi
|
||||
asmcomp/branch_relaxation.cmo : utils/misc.cmi asmcomp/mach.cmi \
|
||||
asmcomp/linearize.cmi asmcomp/cmm.cmi asmcomp/branch_relaxation_intf.cmo \
|
||||
asmcomp/branch_relaxation.cmi
|
||||
|
@ -759,33 +784,43 @@ asmcomp/branch_relaxation.cmx : utils/misc.cmx asmcomp/mach.cmx \
|
|||
asmcomp/branch_relaxation.cmi
|
||||
asmcomp/branch_relaxation_intf.cmo : asmcomp/linearize.cmi asmcomp/arch.cmo
|
||||
asmcomp/branch_relaxation_intf.cmx : asmcomp/linearize.cmx asmcomp/arch.cmx
|
||||
asmcomp/build_export_info.cmo : utils/misc.cmi typing/ident.cmi \
|
||||
asmcomp/export_info.cmi asmcomp/compilenv.cmi utils/clflags.cmi \
|
||||
asmcomp/build_export_info.cmi
|
||||
asmcomp/build_export_info.cmx : utils/misc.cmx typing/ident.cmx \
|
||||
asmcomp/export_info.cmx asmcomp/compilenv.cmx utils/clflags.cmx \
|
||||
asmcomp/build_export_info.cmi
|
||||
asmcomp/clambda.cmo : bytecomp/lambda.cmi typing/ident.cmi \
|
||||
asmcomp/debuginfo.cmi parsing/asttypes.cmi asmcomp/clambda.cmi
|
||||
bytecomp/debuginfo.cmi parsing/asttypes.cmi asmcomp/clambda.cmi
|
||||
asmcomp/clambda.cmx : bytecomp/lambda.cmx typing/ident.cmx \
|
||||
asmcomp/debuginfo.cmx parsing/asttypes.cmi asmcomp/clambda.cmi
|
||||
bytecomp/debuginfo.cmx parsing/asttypes.cmi asmcomp/clambda.cmi
|
||||
asmcomp/closure.cmo : utils/warnings.cmi utils/tbl.cmi bytecomp/switch.cmi \
|
||||
typing/primitive.cmi utils/misc.cmi parsing/location.cmi \
|
||||
bytecomp/lambda.cmi typing/ident.cmi asmcomp/debuginfo.cmi \
|
||||
asmcomp/compilenv.cmi utils/clflags.cmi asmcomp/clambda.cmi \
|
||||
parsing/asttypes.cmi asmcomp/arch.cmo asmcomp/closure.cmi
|
||||
bytecomp/lambda.cmi typing/ident.cmi typing/env.cmi \
|
||||
bytecomp/debuginfo.cmi asmcomp/compilenv.cmi utils/clflags.cmi \
|
||||
asmcomp/clambda.cmi parsing/asttypes.cmi asmcomp/arch.cmo \
|
||||
asmcomp/closure.cmi
|
||||
asmcomp/closure.cmx : utils/warnings.cmx utils/tbl.cmx bytecomp/switch.cmx \
|
||||
typing/primitive.cmx utils/misc.cmx parsing/location.cmx \
|
||||
bytecomp/lambda.cmx typing/ident.cmx asmcomp/debuginfo.cmx \
|
||||
asmcomp/compilenv.cmx utils/clflags.cmx asmcomp/clambda.cmx \
|
||||
parsing/asttypes.cmi asmcomp/arch.cmx asmcomp/closure.cmi
|
||||
asmcomp/cmm.cmo : bytecomp/lambda.cmi typing/ident.cmi asmcomp/debuginfo.cmi \
|
||||
asmcomp/arch.cmo asmcomp/cmm.cmi
|
||||
asmcomp/cmm.cmx : bytecomp/lambda.cmx typing/ident.cmx asmcomp/debuginfo.cmx \
|
||||
asmcomp/arch.cmx asmcomp/cmm.cmi
|
||||
bytecomp/lambda.cmx typing/ident.cmx typing/env.cmx \
|
||||
bytecomp/debuginfo.cmx asmcomp/compilenv.cmx utils/clflags.cmx \
|
||||
asmcomp/clambda.cmx parsing/asttypes.cmi asmcomp/arch.cmx \
|
||||
asmcomp/closure.cmi
|
||||
asmcomp/closure_offsets.cmo : utils/misc.cmi asmcomp/closure_offsets.cmi
|
||||
asmcomp/closure_offsets.cmx : utils/misc.cmx asmcomp/closure_offsets.cmi
|
||||
asmcomp/cmm.cmo : bytecomp/lambda.cmi typing/ident.cmi \
|
||||
bytecomp/debuginfo.cmi asmcomp/arch.cmo asmcomp/cmm.cmi
|
||||
asmcomp/cmm.cmx : bytecomp/lambda.cmx typing/ident.cmx \
|
||||
bytecomp/debuginfo.cmx asmcomp/arch.cmx asmcomp/cmm.cmi
|
||||
asmcomp/cmmgen.cmo : typing/types.cmi bytecomp/switch.cmi \
|
||||
asmcomp/strmatch.cmi asmcomp/proc.cmi typing/primitive.cmi utils/misc.cmi \
|
||||
bytecomp/lambda.cmi typing/ident.cmi asmcomp/debuginfo.cmi \
|
||||
bytecomp/lambda.cmi typing/ident.cmi bytecomp/debuginfo.cmi \
|
||||
utils/config.cmi asmcomp/compilenv.cmi asmcomp/cmx_format.cmi \
|
||||
asmcomp/cmm.cmi utils/clflags.cmi asmcomp/clambda.cmi \
|
||||
parsing/asttypes.cmi asmcomp/arch.cmo asmcomp/cmmgen.cmi
|
||||
asmcomp/cmmgen.cmx : typing/types.cmx bytecomp/switch.cmx \
|
||||
asmcomp/strmatch.cmx asmcomp/proc.cmx typing/primitive.cmx utils/misc.cmx \
|
||||
bytecomp/lambda.cmx typing/ident.cmx asmcomp/debuginfo.cmx \
|
||||
bytecomp/lambda.cmx typing/ident.cmx bytecomp/debuginfo.cmx \
|
||||
utils/config.cmx asmcomp/compilenv.cmx asmcomp/cmx_format.cmi \
|
||||
asmcomp/cmm.cmx utils/clflags.cmx asmcomp/clambda.cmx \
|
||||
parsing/asttypes.cmi asmcomp/arch.cmx asmcomp/cmmgen.cmi
|
||||
|
@ -795,47 +830,61 @@ asmcomp/comballoc.cmo : asmcomp/reg.cmi asmcomp/mach.cmi utils/config.cmi \
|
|||
asmcomp/arch.cmo asmcomp/comballoc.cmi
|
||||
asmcomp/comballoc.cmx : asmcomp/reg.cmx asmcomp/mach.cmx utils/config.cmx \
|
||||
asmcomp/arch.cmx asmcomp/comballoc.cmi
|
||||
asmcomp/compilenv.cmo : utils/misc.cmi parsing/location.cmi typing/ident.cmi \
|
||||
typing/env.cmi utils/config.cmi asmcomp/cmx_format.cmi \
|
||||
asmcomp/clambda.cmi asmcomp/compilenv.cmi
|
||||
asmcomp/compilenv.cmx : utils/misc.cmx parsing/location.cmx typing/ident.cmx \
|
||||
typing/env.cmx utils/config.cmx asmcomp/cmx_format.cmi \
|
||||
asmcomp/clambda.cmx asmcomp/compilenv.cmi
|
||||
asmcomp/compilenv.cmo : utils/warnings.cmi utils/misc.cmi \
|
||||
parsing/location.cmi typing/ident.cmi typing/env.cmi utils/config.cmi \
|
||||
asmcomp/cmx_format.cmi asmcomp/clambda.cmi asmcomp/compilenv.cmi
|
||||
asmcomp/compilenv.cmx : utils/warnings.cmx utils/misc.cmx \
|
||||
parsing/location.cmx typing/ident.cmx typing/env.cmx utils/config.cmx \
|
||||
asmcomp/cmx_format.cmi asmcomp/clambda.cmx asmcomp/compilenv.cmi
|
||||
asmcomp/deadcode.cmo : asmcomp/reg.cmi asmcomp/proc.cmi asmcomp/mach.cmi \
|
||||
asmcomp/deadcode.cmi
|
||||
asmcomp/deadcode.cmx : asmcomp/reg.cmx asmcomp/proc.cmx asmcomp/mach.cmx \
|
||||
asmcomp/deadcode.cmi
|
||||
asmcomp/debuginfo.cmo : parsing/location.cmi bytecomp/lambda.cmi \
|
||||
asmcomp/debuginfo.cmi
|
||||
asmcomp/debuginfo.cmx : parsing/location.cmx bytecomp/lambda.cmx \
|
||||
asmcomp/debuginfo.cmi
|
||||
asmcomp/emit.cmo : asmcomp/x86_proc.cmi asmcomp/x86_masm.cmi \
|
||||
asmcomp/x86_gas.cmi asmcomp/x86_dsl.cmi asmcomp/x86_ast.cmi \
|
||||
asmcomp/reg.cmi asmcomp/proc.cmi utils/misc.cmi asmcomp/mach.cmi \
|
||||
asmcomp/linearize.cmi bytecomp/lambda.cmi asmcomp/emitaux.cmi \
|
||||
asmcomp/debuginfo.cmi utils/config.cmi asmcomp/compilenv.cmi \
|
||||
bytecomp/debuginfo.cmi utils/config.cmi asmcomp/compilenv.cmi \
|
||||
asmcomp/cmm.cmi utils/clflags.cmi asmcomp/branch_relaxation.cmi \
|
||||
asmcomp/arch.cmo asmcomp/emit.cmi
|
||||
asmcomp/emit.cmx : asmcomp/x86_proc.cmx asmcomp/x86_masm.cmx \
|
||||
asmcomp/x86_gas.cmx asmcomp/x86_dsl.cmx asmcomp/x86_ast.cmi \
|
||||
asmcomp/reg.cmx asmcomp/proc.cmx utils/misc.cmx asmcomp/mach.cmx \
|
||||
asmcomp/linearize.cmx bytecomp/lambda.cmx asmcomp/emitaux.cmx \
|
||||
asmcomp/debuginfo.cmx utils/config.cmx asmcomp/compilenv.cmx \
|
||||
bytecomp/debuginfo.cmx utils/config.cmx asmcomp/compilenv.cmx \
|
||||
asmcomp/cmm.cmx utils/clflags.cmx asmcomp/branch_relaxation.cmx \
|
||||
asmcomp/arch.cmx asmcomp/emit.cmi
|
||||
asmcomp/emitaux.cmo : asmcomp/linearize.cmi asmcomp/debuginfo.cmi \
|
||||
asmcomp/emitaux.cmo : asmcomp/linearize.cmi bytecomp/debuginfo.cmi \
|
||||
utils/config.cmi utils/clflags.cmi asmcomp/arch.cmo asmcomp/emitaux.cmi
|
||||
asmcomp/emitaux.cmx : asmcomp/linearize.cmx asmcomp/debuginfo.cmx \
|
||||
asmcomp/emitaux.cmx : asmcomp/linearize.cmx bytecomp/debuginfo.cmx \
|
||||
utils/config.cmx utils/clflags.cmx asmcomp/arch.cmx asmcomp/emitaux.cmi
|
||||
asmcomp/export_info.cmo : typing/ident.cmi asmcomp/export_info.cmi
|
||||
asmcomp/export_info.cmx : typing/ident.cmx asmcomp/export_info.cmi
|
||||
asmcomp/export_info_for_pack.cmo : utils/misc.cmi typing/ident.cmi \
|
||||
asmcomp/export_info.cmi asmcomp/export_info_for_pack.cmi
|
||||
asmcomp/export_info_for_pack.cmx : utils/misc.cmx typing/ident.cmx \
|
||||
asmcomp/export_info.cmx asmcomp/export_info_for_pack.cmi
|
||||
asmcomp/flambda_to_clambda.cmo : typing/primitive.cmi utils/numbers.cmi \
|
||||
utils/misc.cmi typing/ident.cmi asmcomp/export_info.cmi \
|
||||
bytecomp/debuginfo.cmi asmcomp/compilenv.cmi asmcomp/closure_offsets.cmi \
|
||||
utils/clflags.cmi asmcomp/clambda.cmi asmcomp/flambda_to_clambda.cmi
|
||||
asmcomp/flambda_to_clambda.cmx : typing/primitive.cmx utils/numbers.cmx \
|
||||
utils/misc.cmx typing/ident.cmx asmcomp/export_info.cmx \
|
||||
bytecomp/debuginfo.cmx asmcomp/compilenv.cmx asmcomp/closure_offsets.cmx \
|
||||
utils/clflags.cmx asmcomp/clambda.cmx asmcomp/flambda_to_clambda.cmi
|
||||
asmcomp/import_approx.cmo : utils/misc.cmi asmcomp/export_info.cmi \
|
||||
asmcomp/compilenv.cmi asmcomp/import_approx.cmi
|
||||
asmcomp/import_approx.cmx : utils/misc.cmx asmcomp/export_info.cmx \
|
||||
asmcomp/compilenv.cmx asmcomp/import_approx.cmi
|
||||
asmcomp/interf.cmo : asmcomp/reg.cmi asmcomp/proc.cmi asmcomp/mach.cmi \
|
||||
asmcomp/interf.cmi
|
||||
asmcomp/interf.cmx : asmcomp/reg.cmx asmcomp/proc.cmx asmcomp/mach.cmx \
|
||||
asmcomp/interf.cmi
|
||||
asmcomp/linearize.cmo : asmcomp/reg.cmi asmcomp/proc.cmi utils/misc.cmi \
|
||||
asmcomp/mach.cmi bytecomp/lambda.cmi asmcomp/debuginfo.cmi \
|
||||
asmcomp/mach.cmi bytecomp/lambda.cmi bytecomp/debuginfo.cmi \
|
||||
asmcomp/cmm.cmi asmcomp/linearize.cmi
|
||||
asmcomp/linearize.cmx : asmcomp/reg.cmx asmcomp/proc.cmx utils/misc.cmx \
|
||||
asmcomp/mach.cmx bytecomp/lambda.cmx asmcomp/debuginfo.cmx \
|
||||
asmcomp/mach.cmx bytecomp/lambda.cmx bytecomp/debuginfo.cmx \
|
||||
asmcomp/cmm.cmx asmcomp/linearize.cmi
|
||||
asmcomp/liveness.cmo : asmcomp/reg.cmi asmcomp/proc.cmi \
|
||||
asmcomp/printmach.cmi utils/misc.cmi asmcomp/mach.cmi \
|
||||
|
@ -843,10 +892,10 @@ asmcomp/liveness.cmo : asmcomp/reg.cmi asmcomp/proc.cmi \
|
|||
asmcomp/liveness.cmx : asmcomp/reg.cmx asmcomp/proc.cmx \
|
||||
asmcomp/printmach.cmx utils/misc.cmx asmcomp/mach.cmx \
|
||||
asmcomp/liveness.cmi
|
||||
asmcomp/mach.cmo : asmcomp/reg.cmi bytecomp/lambda.cmi asmcomp/debuginfo.cmi \
|
||||
asmcomp/cmm.cmi asmcomp/arch.cmo asmcomp/mach.cmi
|
||||
asmcomp/mach.cmx : asmcomp/reg.cmx bytecomp/lambda.cmx asmcomp/debuginfo.cmx \
|
||||
asmcomp/cmm.cmx asmcomp/arch.cmx asmcomp/mach.cmi
|
||||
asmcomp/mach.cmo : asmcomp/reg.cmi bytecomp/lambda.cmi \
|
||||
bytecomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/arch.cmo asmcomp/mach.cmi
|
||||
asmcomp/mach.cmx : asmcomp/reg.cmx bytecomp/lambda.cmx \
|
||||
bytecomp/debuginfo.cmx asmcomp/cmm.cmx asmcomp/arch.cmx asmcomp/mach.cmi
|
||||
asmcomp/printclambda.cmo : bytecomp/printlambda.cmi bytecomp/lambda.cmi \
|
||||
typing/ident.cmi asmcomp/clambda.cmi parsing/asttypes.cmi \
|
||||
asmcomp/printclambda.cmi
|
||||
|
@ -854,22 +903,22 @@ asmcomp/printclambda.cmx : bytecomp/printlambda.cmx bytecomp/lambda.cmx \
|
|||
typing/ident.cmx asmcomp/clambda.cmx parsing/asttypes.cmi \
|
||||
asmcomp/printclambda.cmi
|
||||
asmcomp/printcmm.cmo : bytecomp/lambda.cmi typing/ident.cmi \
|
||||
asmcomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/printcmm.cmi
|
||||
bytecomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/printcmm.cmi
|
||||
asmcomp/printcmm.cmx : bytecomp/lambda.cmx typing/ident.cmx \
|
||||
asmcomp/debuginfo.cmx asmcomp/cmm.cmx asmcomp/printcmm.cmi
|
||||
bytecomp/debuginfo.cmx asmcomp/cmm.cmx asmcomp/printcmm.cmi
|
||||
asmcomp/printlinear.cmo : asmcomp/printmach.cmi asmcomp/mach.cmi \
|
||||
asmcomp/linearize.cmi bytecomp/lambda.cmi asmcomp/debuginfo.cmi \
|
||||
asmcomp/linearize.cmi bytecomp/lambda.cmi bytecomp/debuginfo.cmi \
|
||||
asmcomp/printlinear.cmi
|
||||
asmcomp/printlinear.cmx : asmcomp/printmach.cmx asmcomp/mach.cmx \
|
||||
asmcomp/linearize.cmx bytecomp/lambda.cmx asmcomp/debuginfo.cmx \
|
||||
asmcomp/linearize.cmx bytecomp/lambda.cmx bytecomp/debuginfo.cmx \
|
||||
asmcomp/printlinear.cmi
|
||||
asmcomp/printmach.cmo : asmcomp/reg.cmi asmcomp/proc.cmi \
|
||||
asmcomp/printcmm.cmi asmcomp/mach.cmi bytecomp/lambda.cmi \
|
||||
asmcomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/arch.cmo \
|
||||
bytecomp/debuginfo.cmi asmcomp/cmm.cmi asmcomp/arch.cmo \
|
||||
asmcomp/printmach.cmi
|
||||
asmcomp/printmach.cmx : asmcomp/reg.cmx asmcomp/proc.cmx \
|
||||
asmcomp/printcmm.cmx asmcomp/mach.cmx bytecomp/lambda.cmx \
|
||||
asmcomp/debuginfo.cmx asmcomp/cmm.cmx asmcomp/arch.cmx \
|
||||
bytecomp/debuginfo.cmx asmcomp/cmm.cmx asmcomp/arch.cmx \
|
||||
asmcomp/printmach.cmi
|
||||
asmcomp/proc.cmo : asmcomp/x86_proc.cmi asmcomp/reg.cmi utils/misc.cmi \
|
||||
asmcomp/mach.cmi utils/config.cmi asmcomp/cmm.cmi asmcomp/arch.cmo \
|
||||
|
@ -897,11 +946,11 @@ asmcomp/scheduling.cmo : asmcomp/schedgen.cmi asmcomp/scheduling.cmi
|
|||
asmcomp/scheduling.cmx : asmcomp/schedgen.cmx asmcomp/scheduling.cmi
|
||||
asmcomp/selectgen.cmo : utils/tbl.cmi bytecomp/simplif.cmi asmcomp/reg.cmi \
|
||||
asmcomp/proc.cmi utils/misc.cmi asmcomp/mach.cmi bytecomp/lambda.cmi \
|
||||
typing/ident.cmi asmcomp/debuginfo.cmi asmcomp/cmm.cmi utils/clflags.cmi \
|
||||
typing/ident.cmi bytecomp/debuginfo.cmi asmcomp/cmm.cmi utils/clflags.cmi \
|
||||
asmcomp/arch.cmo asmcomp/selectgen.cmi
|
||||
asmcomp/selectgen.cmx : utils/tbl.cmx bytecomp/simplif.cmx asmcomp/reg.cmx \
|
||||
asmcomp/proc.cmx utils/misc.cmx asmcomp/mach.cmx bytecomp/lambda.cmx \
|
||||
typing/ident.cmx asmcomp/debuginfo.cmx asmcomp/cmm.cmx utils/clflags.cmx \
|
||||
typing/ident.cmx bytecomp/debuginfo.cmx asmcomp/cmm.cmx utils/clflags.cmx \
|
||||
asmcomp/arch.cmx asmcomp/selectgen.cmi
|
||||
asmcomp/selection.cmo : asmcomp/selectgen.cmi asmcomp/proc.cmi \
|
||||
asmcomp/mach.cmi asmcomp/cmm.cmi utils/clflags.cmi asmcomp/arch.cmo \
|
||||
|
@ -921,6 +970,14 @@ asmcomp/strmatch.cmo : bytecomp/lambda.cmi typing/ident.cmi asmcomp/cmm.cmi \
|
|||
asmcomp/arch.cmo asmcomp/strmatch.cmi
|
||||
asmcomp/strmatch.cmx : bytecomp/lambda.cmx typing/ident.cmx asmcomp/cmm.cmx \
|
||||
asmcomp/arch.cmx asmcomp/strmatch.cmi
|
||||
asmcomp/un_anf.cmo : asmcomp/printclambda.cmi utils/misc.cmi \
|
||||
bytecomp/lambda.cmi typing/ident.cmi bytecomp/debuginfo.cmi \
|
||||
utils/config.cmi utils/clflags.cmi asmcomp/clambda.cmi \
|
||||
parsing/asttypes.cmi asmcomp/un_anf.cmi
|
||||
asmcomp/un_anf.cmx : asmcomp/printclambda.cmx utils/misc.cmx \
|
||||
bytecomp/lambda.cmx typing/ident.cmx bytecomp/debuginfo.cmx \
|
||||
utils/config.cmx utils/clflags.cmx asmcomp/clambda.cmx \
|
||||
parsing/asttypes.cmi asmcomp/un_anf.cmi
|
||||
asmcomp/x86_dsl.cmo : asmcomp/x86_proc.cmi asmcomp/x86_ast.cmi \
|
||||
asmcomp/x86_dsl.cmi
|
||||
asmcomp/x86_dsl.cmx : asmcomp/x86_proc.cmx asmcomp/x86_ast.cmi \
|
||||
|
@ -953,22 +1010,22 @@ driver/compenv.cmx : utils/warnings.cmx utils/misc.cmx parsing/location.cmx \
|
|||
utils/config.cmx utils/clflags.cmx driver/compenv.cmi
|
||||
driver/compile.cmo : utils/warnings.cmi typing/typemod.cmi \
|
||||
typing/typedtree.cmi typing/typecore.cmi bytecomp/translmod.cmi \
|
||||
typing/stypes.cmi bytecomp/simplif.cmi typing/printtyped.cmi \
|
||||
typing/printtyp.cmi bytecomp/printlambda.cmi bytecomp/printinstr.cmi \
|
||||
parsing/printast.cmi parsing/pprintast.cmi driver/pparse.cmi \
|
||||
utils/misc.cmi parsing/location.cmi typing/includemod.cmi typing/env.cmi \
|
||||
bytecomp/emitcode.cmi driver/compmisc.cmi driver/compenv.cmi \
|
||||
utils/clflags.cmi utils/ccomp.cmi bytecomp/bytegen.cmi \
|
||||
parsing/builtin_attributes.cmi driver/compile.cmi
|
||||
utils/timings.cmi typing/stypes.cmi bytecomp/simplif.cmi \
|
||||
typing/printtyped.cmi typing/printtyp.cmi bytecomp/printlambda.cmi \
|
||||
bytecomp/printinstr.cmi parsing/printast.cmi parsing/pprintast.cmi \
|
||||
driver/pparse.cmi utils/misc.cmi parsing/location.cmi \
|
||||
typing/includemod.cmi typing/env.cmi bytecomp/emitcode.cmi \
|
||||
driver/compmisc.cmi driver/compenv.cmi utils/clflags.cmi utils/ccomp.cmi \
|
||||
bytecomp/bytegen.cmi parsing/builtin_attributes.cmi driver/compile.cmi
|
||||
driver/compile.cmx : utils/warnings.cmx typing/typemod.cmx \
|
||||
typing/typedtree.cmx typing/typecore.cmx bytecomp/translmod.cmx \
|
||||
typing/stypes.cmx bytecomp/simplif.cmx typing/printtyped.cmx \
|
||||
typing/printtyp.cmx bytecomp/printlambda.cmx bytecomp/printinstr.cmx \
|
||||
parsing/printast.cmx parsing/pprintast.cmx driver/pparse.cmx \
|
||||
utils/misc.cmx parsing/location.cmx typing/includemod.cmx typing/env.cmx \
|
||||
bytecomp/emitcode.cmx driver/compmisc.cmx driver/compenv.cmx \
|
||||
utils/clflags.cmx utils/ccomp.cmx bytecomp/bytegen.cmx \
|
||||
parsing/builtin_attributes.cmx driver/compile.cmi
|
||||
utils/timings.cmx typing/stypes.cmx bytecomp/simplif.cmx \
|
||||
typing/printtyped.cmx typing/printtyp.cmx bytecomp/printlambda.cmx \
|
||||
bytecomp/printinstr.cmx parsing/printast.cmx parsing/pprintast.cmx \
|
||||
driver/pparse.cmx utils/misc.cmx parsing/location.cmx \
|
||||
typing/includemod.cmx typing/env.cmx bytecomp/emitcode.cmx \
|
||||
driver/compmisc.cmx driver/compenv.cmx utils/clflags.cmx utils/ccomp.cmx \
|
||||
bytecomp/bytegen.cmx parsing/builtin_attributes.cmx driver/compile.cmi
|
||||
driver/compmisc.cmo : typing/typemod.cmi utils/misc.cmi \
|
||||
parsing/longident.cmi parsing/location.cmi typing/ident.cmi \
|
||||
typing/env.cmi utils/config.cmi driver/compenv.cmi utils/clflags.cmi \
|
||||
|
@ -979,56 +1036,56 @@ driver/compmisc.cmx : typing/typemod.cmx utils/misc.cmx \
|
|||
parsing/asttypes.cmi driver/compmisc.cmi
|
||||
driver/errors.cmo : parsing/location.cmi driver/errors.cmi
|
||||
driver/errors.cmx : parsing/location.cmx driver/errors.cmi
|
||||
driver/main.cmo : utils/warnings.cmi utils/misc.cmi driver/main_args.cmi \
|
||||
parsing/location.cmi utils/config.cmi driver/compmisc.cmi \
|
||||
driver/compile.cmi driver/compenv.cmi utils/clflags.cmi \
|
||||
bytecomp/bytepackager.cmi bytecomp/bytelink.cmi \
|
||||
driver/main.cmo : utils/warnings.cmi utils/timings.cmi utils/misc.cmi \
|
||||
driver/main_args.cmi parsing/location.cmi utils/config.cmi \
|
||||
driver/compmisc.cmi driver/compile.cmi driver/compenv.cmi \
|
||||
utils/clflags.cmi bytecomp/bytepackager.cmi bytecomp/bytelink.cmi \
|
||||
bytecomp/bytelibrarian.cmi driver/main.cmi
|
||||
driver/main.cmx : utils/warnings.cmx utils/misc.cmx driver/main_args.cmx \
|
||||
parsing/location.cmx utils/config.cmx driver/compmisc.cmx \
|
||||
driver/compile.cmx driver/compenv.cmx utils/clflags.cmx \
|
||||
bytecomp/bytepackager.cmx bytecomp/bytelink.cmx \
|
||||
driver/main.cmx : utils/warnings.cmx utils/timings.cmx utils/misc.cmx \
|
||||
driver/main_args.cmx parsing/location.cmx utils/config.cmx \
|
||||
driver/compmisc.cmx driver/compile.cmx driver/compenv.cmx \
|
||||
utils/clflags.cmx bytecomp/bytepackager.cmx bytecomp/bytelink.cmx \
|
||||
bytecomp/bytelibrarian.cmx driver/main.cmi
|
||||
driver/main_args.cmo : utils/warnings.cmi driver/main_args.cmi
|
||||
driver/main_args.cmx : utils/warnings.cmx driver/main_args.cmi
|
||||
driver/optcompile.cmo : utils/warnings.cmi typing/typemod.cmi \
|
||||
typing/typedtree.cmi typing/typecore.cmi bytecomp/translmod.cmi \
|
||||
typing/stypes.cmi bytecomp/simplif.cmi typing/printtyped.cmi \
|
||||
typing/printtyp.cmi bytecomp/printlambda.cmi parsing/printast.cmi \
|
||||
parsing/pprintast.cmi driver/pparse.cmi utils/misc.cmi \
|
||||
typing/includemod.cmi typing/env.cmi utils/config.cmi driver/compmisc.cmi \
|
||||
asmcomp/compilenv.cmi driver/compenv.cmi utils/clflags.cmi \
|
||||
utils/ccomp.cmi parsing/builtin_attributes.cmi asmcomp/asmgen.cmi \
|
||||
driver/optcompile.cmi
|
||||
utils/timings.cmi typing/stypes.cmi bytecomp/simplif.cmi \
|
||||
typing/printtyped.cmi typing/printtyp.cmi bytecomp/printlambda.cmi \
|
||||
parsing/printast.cmi parsing/pprintast.cmi driver/pparse.cmi \
|
||||
utils/misc.cmi typing/includemod.cmi typing/env.cmi utils/config.cmi \
|
||||
driver/compmisc.cmi asmcomp/compilenv.cmi driver/compenv.cmi \
|
||||
utils/clflags.cmi utils/ccomp.cmi parsing/builtin_attributes.cmi \
|
||||
asmcomp/asmgen.cmi driver/optcompile.cmi
|
||||
driver/optcompile.cmx : utils/warnings.cmx typing/typemod.cmx \
|
||||
typing/typedtree.cmx typing/typecore.cmx bytecomp/translmod.cmx \
|
||||
typing/stypes.cmx bytecomp/simplif.cmx typing/printtyped.cmx \
|
||||
typing/printtyp.cmx bytecomp/printlambda.cmx parsing/printast.cmx \
|
||||
parsing/pprintast.cmx driver/pparse.cmx utils/misc.cmx \
|
||||
typing/includemod.cmx typing/env.cmx utils/config.cmx driver/compmisc.cmx \
|
||||
asmcomp/compilenv.cmx driver/compenv.cmx utils/clflags.cmx \
|
||||
utils/ccomp.cmx parsing/builtin_attributes.cmx asmcomp/asmgen.cmx \
|
||||
driver/optcompile.cmi
|
||||
utils/timings.cmx typing/stypes.cmx bytecomp/simplif.cmx \
|
||||
typing/printtyped.cmx typing/printtyp.cmx bytecomp/printlambda.cmx \
|
||||
parsing/printast.cmx parsing/pprintast.cmx driver/pparse.cmx \
|
||||
utils/misc.cmx typing/includemod.cmx typing/env.cmx utils/config.cmx \
|
||||
driver/compmisc.cmx asmcomp/compilenv.cmx driver/compenv.cmx \
|
||||
utils/clflags.cmx utils/ccomp.cmx parsing/builtin_attributes.cmx \
|
||||
asmcomp/asmgen.cmx driver/optcompile.cmi
|
||||
driver/opterrors.cmo : parsing/location.cmi driver/opterrors.cmi
|
||||
driver/opterrors.cmx : parsing/location.cmx driver/opterrors.cmi
|
||||
driver/optmain.cmo : utils/warnings.cmi asmcomp/printmach.cmi \
|
||||
driver/optcompile.cmi utils/misc.cmi driver/main_args.cmi \
|
||||
parsing/location.cmi utils/config.cmi driver/compmisc.cmi \
|
||||
driver/compenv.cmi utils/clflags.cmi asmcomp/asmpackager.cmi \
|
||||
asmcomp/asmlink.cmi asmcomp/asmlibrarian.cmi asmcomp/arch.cmo \
|
||||
driver/optmain.cmi
|
||||
driver/optmain.cmx : utils/warnings.cmx asmcomp/printmach.cmx \
|
||||
driver/optcompile.cmx utils/misc.cmx driver/main_args.cmx \
|
||||
parsing/location.cmx utils/config.cmx driver/compmisc.cmx \
|
||||
driver/compenv.cmx utils/clflags.cmx asmcomp/asmpackager.cmx \
|
||||
asmcomp/asmlink.cmx asmcomp/asmlibrarian.cmx asmcomp/arch.cmx \
|
||||
driver/optmain.cmi
|
||||
driver/pparse.cmo : parsing/parse.cmi utils/misc.cmi parsing/location.cmi \
|
||||
utils/config.cmi utils/clflags.cmi utils/ccomp.cmi parsing/ast_mapper.cmi \
|
||||
driver/pparse.cmi
|
||||
driver/pparse.cmx : parsing/parse.cmx utils/misc.cmx parsing/location.cmx \
|
||||
utils/config.cmx utils/clflags.cmx utils/ccomp.cmx parsing/ast_mapper.cmx \
|
||||
driver/pparse.cmi
|
||||
driver/optmain.cmo : utils/warnings.cmi utils/timings.cmi \
|
||||
asmcomp/printmach.cmi driver/optcompile.cmi utils/misc.cmi \
|
||||
driver/main_args.cmi parsing/location.cmi utils/config.cmi \
|
||||
driver/compmisc.cmi driver/compenv.cmi utils/clflags.cmi \
|
||||
asmcomp/asmpackager.cmi asmcomp/asmlink.cmi asmcomp/asmlibrarian.cmi \
|
||||
asmcomp/arch.cmo driver/optmain.cmi
|
||||
driver/optmain.cmx : utils/warnings.cmx utils/timings.cmx \
|
||||
asmcomp/printmach.cmx driver/optcompile.cmx utils/misc.cmx \
|
||||
driver/main_args.cmx parsing/location.cmx utils/config.cmx \
|
||||
driver/compmisc.cmx driver/compenv.cmx utils/clflags.cmx \
|
||||
asmcomp/asmpackager.cmx asmcomp/asmlink.cmx asmcomp/asmlibrarian.cmx \
|
||||
asmcomp/arch.cmx driver/optmain.cmi
|
||||
driver/pparse.cmo : utils/timings.cmi parsing/parse.cmi utils/misc.cmi \
|
||||
parsing/location.cmi utils/config.cmi utils/clflags.cmi utils/ccomp.cmi \
|
||||
parsing/ast_mapper.cmi driver/pparse.cmi
|
||||
driver/pparse.cmx : utils/timings.cmx parsing/parse.cmx utils/misc.cmx \
|
||||
parsing/location.cmx utils/config.cmx utils/clflags.cmx utils/ccomp.cmx \
|
||||
parsing/ast_mapper.cmx driver/pparse.cmi
|
||||
toplevel/genprintval.cmi : typing/types.cmi typing/path.cmi \
|
||||
typing/outcometree.cmi typing/env.cmi
|
||||
toplevel/opttopdirs.cmi : parsing/longident.cmi
|
||||
|
@ -1069,28 +1126,28 @@ toplevel/opttopdirs.cmx : utils/warnings.cmx typing/types.cmx \
|
|||
toplevel/opttopdirs.cmi
|
||||
toplevel/opttoploop.cmo : utils/warnings.cmi typing/types.cmi \
|
||||
typing/typemod.cmi typing/typedtree.cmi typing/typecore.cmi \
|
||||
bytecomp/translmod.cmi bytecomp/simplif.cmi typing/printtyped.cmi \
|
||||
typing/printtyp.cmi bytecomp/printlambda.cmi parsing/printast.cmi \
|
||||
typing/predef.cmi parsing/pprintast.cmi typing/path.cmi \
|
||||
parsing/parsetree.cmi parsing/parse.cmi typing/outcometree.cmi \
|
||||
typing/oprint.cmi utils/misc.cmi parsing/longident.cmi \
|
||||
parsing/location.cmi parsing/lexer.cmi typing/ident.cmi \
|
||||
toplevel/genprintval.cmi typing/env.cmi utils/config.cmi \
|
||||
driver/compmisc.cmi asmcomp/compilenv.cmi utils/clflags.cmi \
|
||||
typing/btype.cmi asmcomp/asmlink.cmi asmcomp/asmgen.cmi \
|
||||
toplevel/opttoploop.cmi
|
||||
bytecomp/translmod.cmi utils/timings.cmi bytecomp/simplif.cmi \
|
||||
typing/printtyped.cmi typing/printtyp.cmi bytecomp/printlambda.cmi \
|
||||
parsing/printast.cmi typing/predef.cmi parsing/pprintast.cmi \
|
||||
driver/pparse.cmi typing/path.cmi parsing/parsetree.cmi parsing/parse.cmi \
|
||||
typing/outcometree.cmi typing/oprint.cmi utils/misc.cmi \
|
||||
parsing/longident.cmi parsing/location.cmi parsing/lexer.cmi \
|
||||
typing/includemod.cmi typing/ident.cmi toplevel/genprintval.cmi \
|
||||
typing/env.cmi utils/config.cmi driver/compmisc.cmi asmcomp/compilenv.cmi \
|
||||
utils/clflags.cmi typing/btype.cmi parsing/ast_helper.cmi \
|
||||
asmcomp/asmlink.cmi asmcomp/asmgen.cmi toplevel/opttoploop.cmi
|
||||
toplevel/opttoploop.cmx : utils/warnings.cmx typing/types.cmx \
|
||||
typing/typemod.cmx typing/typedtree.cmx typing/typecore.cmx \
|
||||
bytecomp/translmod.cmx bytecomp/simplif.cmx typing/printtyped.cmx \
|
||||
typing/printtyp.cmx bytecomp/printlambda.cmx parsing/printast.cmx \
|
||||
typing/predef.cmx parsing/pprintast.cmx typing/path.cmx \
|
||||
parsing/parsetree.cmi parsing/parse.cmx typing/outcometree.cmi \
|
||||
typing/oprint.cmx utils/misc.cmx parsing/longident.cmx \
|
||||
parsing/location.cmx parsing/lexer.cmx typing/ident.cmx \
|
||||
toplevel/genprintval.cmx typing/env.cmx utils/config.cmx \
|
||||
driver/compmisc.cmx asmcomp/compilenv.cmx utils/clflags.cmx \
|
||||
typing/btype.cmx asmcomp/asmlink.cmx asmcomp/asmgen.cmx \
|
||||
toplevel/opttoploop.cmi
|
||||
bytecomp/translmod.cmx utils/timings.cmx bytecomp/simplif.cmx \
|
||||
typing/printtyped.cmx typing/printtyp.cmx bytecomp/printlambda.cmx \
|
||||
parsing/printast.cmx typing/predef.cmx parsing/pprintast.cmx \
|
||||
driver/pparse.cmx typing/path.cmx parsing/parsetree.cmi parsing/parse.cmx \
|
||||
typing/outcometree.cmi typing/oprint.cmx utils/misc.cmx \
|
||||
parsing/longident.cmx parsing/location.cmx parsing/lexer.cmx \
|
||||
typing/includemod.cmx typing/ident.cmx toplevel/genprintval.cmx \
|
||||
typing/env.cmx utils/config.cmx driver/compmisc.cmx asmcomp/compilenv.cmx \
|
||||
utils/clflags.cmx typing/btype.cmx parsing/ast_helper.cmx \
|
||||
asmcomp/asmlink.cmx asmcomp/asmgen.cmx toplevel/opttoploop.cmi
|
||||
toplevel/opttopmain.cmo : utils/warnings.cmi asmcomp/printmach.cmi \
|
||||
toplevel/opttoploop.cmi toplevel/opttopdirs.cmi utils/misc.cmi \
|
||||
driver/main_args.cmi parsing/location.cmi utils/config.cmi \
|
||||
|
|
|
@ -127,6 +127,9 @@
|
|||
/lex/ocamllex.opt
|
||||
/lex/parser.output
|
||||
|
||||
/manual/manual/cmds/warnings-help.etex
|
||||
/manual/manual/warnings-help.etex
|
||||
|
||||
/ocamlbuild/ocamlbuild_config.ml
|
||||
/ocamlbuild/lexers.ml
|
||||
/ocamlbuild/glob_lexer.ml
|
||||
|
@ -197,8 +200,8 @@
|
|||
|
||||
/stdlib/camlheader
|
||||
/stdlib/target_camlheader
|
||||
/stdlib/camlheaderd
|
||||
/stdlib/target_camlheaderd
|
||||
/stdlib/camlheader[di]
|
||||
/stdlib/target_camlheader[di]
|
||||
/stdlib/camlheader_ur
|
||||
/stdlib/labelled-*
|
||||
/stdlib/caml
|
||||
|
@ -254,6 +257,7 @@
|
|||
/testsuite/tests/unboxed-primitive-args/stubs.c
|
||||
|
||||
/testsuite/tests/warnings/w55.opt.opt_result
|
||||
/testsuite/tests/warnings/w58.opt.opt_result
|
||||
|
||||
/tools/ocamldep
|
||||
/tools/ocamldep.opt
|
||||
|
@ -282,6 +286,8 @@
|
|||
/tools/objinfo_helper
|
||||
/tools/read_cmt
|
||||
/tools/read_cmt.opt
|
||||
/tools/cmpbyt
|
||||
/tools/stripdebug
|
||||
|
||||
/utils/config.ml
|
||||
|
||||
|
|
38
Changes
38
Changes
|
@ -63,6 +63,7 @@ Compilers:
|
|||
- PR#4080, PR#6537, PR#5333: fix stack overflow in the compiler when -pack'ing
|
||||
a module
|
||||
that includes a module of the same name (Alain Frisch)
|
||||
- PR#4231, PR#5461: (interim solution) warning 31 is now fatal by default
|
||||
- PR#4800: better compilation of tuple assignment (Gabriel Scherer and
|
||||
Alain Frisch)
|
||||
- PR#5995: keep -for-pack into account to name exceptions; document -for-pack
|
||||
|
@ -100,6 +101,8 @@ Compilers:
|
|||
- PR#7067: Performance regression in the native compiler for long
|
||||
nested structures (Alain Frisch, report by Daniel Bünzli, review
|
||||
by Jacques Garrigue)
|
||||
- PR#7097: Strange syntax error message around illegal packaged module signature
|
||||
constraints (Alain Frisch, report by Jun Furuse)
|
||||
- GPR#17: some cmm optimizations of integer operations with constants
|
||||
(Stephen Dolan, review by Pierre Chambart)
|
||||
- GPR#109: new unboxing strategy for float and int references (Vladimir Brankov,
|
||||
|
@ -128,6 +131,11 @@ Compilers:
|
|||
- GPR#282: relax short-paths safety check in presence of module aliases, take
|
||||
penalty into account while building the printing map.
|
||||
(Thomas Refis, Leo White)
|
||||
- GPR#306: Instrument the compiler to debug performance regressions
|
||||
(Pierre Chambart)
|
||||
- GPR#319: add warning for missing cmx files, and extend -opaque option to mli
|
||||
files.
|
||||
(Leo White)
|
||||
- PR#6920: fix debug informations around uses of %apply or %revapply
|
||||
(Jérémie Dimino)
|
||||
|
||||
|
@ -152,6 +160,8 @@ Runtime system:
|
|||
Shinwell, review by Damien Doligez)
|
||||
- GPR#325: Add v=0x400 flag to OCAMLRUNPARAM to display GC stats on exit (Louis
|
||||
Gesbert, review by Alain Frisch)
|
||||
- GPR#297: Several changes to improve the worst-case GC pause time.
|
||||
(Damien Doligez, with help from Leo White and Francois Bobot)
|
||||
|
||||
Standard library:
|
||||
- PR#5197, GPR#63: Arg: allow flags such as --flag=arg as well as --flag arg
|
||||
|
@ -167,11 +177,14 @@ Standard library:
|
|||
(for conformance with ISO C99) (Xavier Leroy)
|
||||
- PR#6390, GPR#36: expose Sys.{int_size,max_wosize} for improved js_of_ocaml
|
||||
portability (Hugo Heuzard)
|
||||
- PR#6449: Add Map.union (Alain Frisch)
|
||||
* PR#6494: Add equal function in modules
|
||||
Bytes, Char, Digest, Int32, Int64, Nativeint, and String
|
||||
(Romain Calascibetta)
|
||||
* PR#6524, GPR#79: Filename: Optional ?perms argument to open_temp_file
|
||||
(Daniel Bünzli, review by Jacques-Pascal Deplaix)
|
||||
* PR#6525, GPR#80: Add Uchar module to the standard library
|
||||
(Daniel Bünzli, review by Yoriyuki Yamagata and Damien Doligez)
|
||||
- PR#6577: improve performance of %L, %l, %n, %S, %C format specifiers
|
||||
(Alain Frisch)
|
||||
- PR#6585: fix memory leak in win32unix/createprocess.c
|
||||
|
@ -217,6 +230,7 @@ Standard library:
|
|||
(Bobot François)
|
||||
- GPR#329: Add exists, for_all, mem and memq functions in Array
|
||||
(Bernhard Schommer)
|
||||
- GPR#356: Add [Format.kasprintf] (Jérémie Dimino, Mark Shinwell)
|
||||
|
||||
Type system:
|
||||
- PR#5545: Type annotations on methods cannot control the choice of abbreviation
|
||||
|
@ -227,6 +241,8 @@ Type system:
|
|||
- PR#6593: Functor application in tests/basic-modules fails after commit 15405
|
||||
|
||||
Toplevel and debugger:
|
||||
- PR#6113: Add descriptions to directives, and display them via #help
|
||||
(Nick Giannarakis, Berke Durak, Francis Southern and Gabriel Scherer)
|
||||
- PR#6396: Warnings-as-errors not properly flushed in the toplevel
|
||||
(Alain Frisch)
|
||||
- PR#6468: toplevel now supports backtraces if invoked with OCAMLRUNPARAM=b
|
||||
|
@ -240,8 +256,10 @@ Toplevel and debugger:
|
|||
(Valentin Gatien-Baron, review by Jérémie Dimino)
|
||||
- PR#7098: Loss of ppx context in toplevel after an exception
|
||||
(Alain Frisch, report by whitequark)
|
||||
- PR#7101: The toplevel does not close in_channel for libraries specified on its command line
|
||||
(Alain Frisch)
|
||||
- PR#7101: The toplevel does not close in_channel for libraries specified on
|
||||
its command line (Alain Frisch)
|
||||
- PR#7119: ocaml doesn't respect [@@@warning] (Alain Frisch, report by
|
||||
Gabriel Radanne)
|
||||
|
||||
Other libraries:
|
||||
* Unix library: channels created by Unix.in_channel_of_descr or
|
||||
|
@ -289,6 +307,12 @@ Manual:
|
|||
subdirectory of the main OCaml source repository. Contributions to
|
||||
the manual are warmly welcome.
|
||||
(François Bobot, review by Florian Angeletti)
|
||||
- MPR#6676: ongoing simplification of the "Language Extensions" section
|
||||
(Alain Frisch, John Whitington)
|
||||
- MPR#7092, GPR#379: Add missing documentation for new 4.03 features
|
||||
(Florian Angeletti)
|
||||
- MPR#7109, GPR#380: Fix bigarray documentation layout
|
||||
(Florian Angeletti, Leo White)
|
||||
|
||||
Bug fixes:
|
||||
- PR#3612: memory leak in bigarray read from file
|
||||
|
@ -395,7 +419,11 @@ Bug fixes:
|
|||
- PR#7075: Fix repetitions in ocamldoc generated documentation
|
||||
(Florian Angeletti)
|
||||
- PR#7082: Object type in recursive module's `with` annotation
|
||||
- PR#7108: ocamldoc, have -html preserve custom/extended html generators
|
||||
(Armaël Guéneau)
|
||||
- PR#7096: ocamldoc uses an incorrect subscript/superscript style
|
||||
- PR#7115: shadowing in a branch of a GADT match breaks unused variable
|
||||
warning (Alain Frisch, report by Valentin Gatien-Baron)
|
||||
- GPR#205: Clear caml_backtrace_last_exn before registering as root
|
||||
(report and fix by Frederic Bour)
|
||||
- GPR#220: minor -dsource error on recursive modules
|
||||
|
@ -419,6 +447,8 @@ Bug fixes:
|
|||
(Jérémie Dimino)
|
||||
- GPR#355: make ocamlnat build again
|
||||
(Jérémie Dimino, Thomas Refis)
|
||||
- GPR#405: fix compilation under Visual Studio 2015
|
||||
(David Allsopp)
|
||||
|
||||
Features wishes:
|
||||
- PR#4518, GPR#29: change location format for reporting errors in ocamldoc
|
||||
|
@ -485,6 +515,10 @@ Features wishes:
|
|||
(Hugo Heuzard)
|
||||
- GPR#308: add experimental support for NetBSD/arm (verified on RaspberryPi)
|
||||
(Rich Neswold)
|
||||
- GPR#365: prevent printing just a single type variable on one side
|
||||
of a type error clash. (Hugo Heuzard)
|
||||
- GPR#383: configure: define _ALL_SOURCE for build on AIX7.1
|
||||
(tkob)
|
||||
|
||||
OCaml 4.02.3 (27 Jul 2015):
|
||||
---------------------------
|
||||
|
|
|
@ -106,6 +106,10 @@ The `configure` script accepts the following options:
|
|||
Compile and install the debug version of the runtimes, useful
|
||||
for debugging C stubs and other low-level code.
|
||||
|
||||
-with-instrumented-runtime::
|
||||
Compile and install the instrumented version of the runtimes,
|
||||
useful mainly for fine-tuning the GC. Works only on Linux.
|
||||
|
||||
-verbose::
|
||||
Verbose output of the configuration tests. Use it if the outcome
|
||||
of configure is not what you were expecting.
|
||||
|
|
2
Makefile
2
Makefile
|
@ -267,6 +267,8 @@ installoptopt:
|
|||
cp ocamlc.opt $(INSTALL_BINDIR)/ocamlc.opt$(EXE)
|
||||
cp ocamlopt.opt $(INSTALL_BINDIR)/ocamlopt.opt$(EXE)
|
||||
cp lex/ocamllex.opt $(INSTALL_BINDIR)/ocamllex.opt$(EXE)
|
||||
cp utils/*.cmx parsing/*.cmx typing/*.cmx bytecomp/*.cmx \
|
||||
driver/*.cmx asmcomp/*.cmx $(INSTALL_COMPLIBDIR)
|
||||
cp compilerlibs/ocamlcommon.cmxa compilerlibs/ocamlcommon.a \
|
||||
compilerlibs/ocamlbytecomp.cmxa compilerlibs/ocamlbytecomp.a \
|
||||
compilerlibs/ocamloptcomp.cmxa compilerlibs/ocamloptcomp.a \
|
||||
|
|
|
@ -127,7 +127,7 @@ opt:
|
|||
|
||||
# Native-code versions of the tools
|
||||
opt.opt: core opt-core ocamlc.opt all ocamlopt.opt ocamllex.opt \
|
||||
ocamltoolsopt ocamltoolsopt.opt $(OCAMLBUILDNATIVE) $(OCAMLDOC_OPT)
|
||||
ocamltoolsopt ocamltoolsopt.opt otherlibrariesopt $(OCAMLBUILDNATIVE) $(OCAMLDOC_OPT)
|
||||
|
||||
# Complete build using fast compilers
|
||||
world.opt: coldstart opt.opt
|
||||
|
@ -203,6 +203,8 @@ installoptopt:
|
|||
cp ocamlc.opt $(INSTALL_BINDIR)/ocamlc.opt$(EXE)
|
||||
cp ocamlopt.opt $(INSTALL_BINDIR)/ocamlopt.opt$(EXE)
|
||||
cp lex/ocamllex.opt $(INSTALL_BINDIR)/ocamllex.opt$(EXE)
|
||||
cp utils/*.cmx parsing/*.cmx typing/*.cmx bytecomp/*.cmx \
|
||||
driver/*.cmx asmcomp/*.cmx $(INSTALL_COMPLIBDIR)
|
||||
cp compilerlibs/ocamlcommon.cmxa compilerlibs/ocamlcommon.$(A) \
|
||||
compilerlibs/ocamlbytecomp.cmxa compilerlibs/ocamlbytecomp.$(A) \
|
||||
compilerlibs/ocamloptcomp.cmxa compilerlibs/ocamloptcomp.$(A) \
|
||||
|
|
|
@ -37,7 +37,9 @@ INCLUDES=-I utils -I parsing -I typing -I bytecomp -I asmcomp -I driver \
|
|||
-I toplevel
|
||||
|
||||
UTILS=utils/config.cmo utils/clflags.cmo \
|
||||
utils/misc.cmo utils/tbl.cmo \
|
||||
utils/misc.cmo \
|
||||
utils/identifiable.cmo utils/numbers.cmo \
|
||||
utils/tbl.cmo utils/timings.cmo \
|
||||
utils/terminfo.cmo utils/ccomp.cmo utils/warnings.cmo \
|
||||
utils/consistbl.cmo
|
||||
|
||||
|
@ -71,6 +73,7 @@ COMP=bytecomp/lambda.cmo bytecomp/printlambda.cmo \
|
|||
bytecomp/translcore.cmo \
|
||||
bytecomp/translclass.cmo bytecomp/translmod.cmo \
|
||||
bytecomp/simplif.cmo bytecomp/runtimedef.cmo \
|
||||
bytecomp/debuginfo.cmo \
|
||||
driver/pparse.cmo driver/main_args.cmo \
|
||||
driver/compenv.cmo driver/compmisc.cmo
|
||||
|
||||
|
@ -98,7 +101,7 @@ endif
|
|||
|
||||
ASMCOMP=\
|
||||
$(ARCH_SPECIFIC_ASMCOMP) \
|
||||
asmcomp/arch.cmo asmcomp/debuginfo.cmo \
|
||||
asmcomp/arch.cmo \
|
||||
asmcomp/cmm.cmo asmcomp/printcmm.cmo \
|
||||
asmcomp/reg.cmo asmcomp/mach.cmo asmcomp/proc.cmo \
|
||||
asmcomp/clambda.cmo asmcomp/printclambda.cmo asmcomp/compilenv.cmo \
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1,4 +1,4 @@
|
|||
4.03.0+dev11-2015-10-19
|
||||
4.03.0+dev12-2015-11-20
|
||||
|
||||
# The version string is the first line of this file.
|
||||
# It must be in the format described in stdlib/sys.mli
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Supported platforms
|
||||
|
||||
Intel and AMD x86 processors in 64-bit mode, a.k.a `x86_64`.
|
||||
|
||||
Floating-point architecture: SSE2, supported by all x86_64 processors.
|
||||
|
||||
Operating systems: Linux, BSD, MacOS X, MS Windows.
|
||||
|
||||
Debian architecture name: `amd64`
|
||||
|
||||
# Reference documents
|
||||
|
||||
* Instruction set architecture:
|
||||
any Intel or AMD manual less than 10 years old.
|
||||
* ELF application binary interface:
|
||||
_System V Application Binary Interface,
|
||||
AMD64 Architecture Processor Supplement_
|
||||
* MacOS X application binary interface:
|
||||
_OS X ABI Function Call Guide: x86-64 Function Calling Conventions_
|
||||
* Windows 64 application binary interface:
|
||||
_x64 Software Conventions_ from MSDN
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Supported platforms
|
||||
|
||||
A great many variants of the ARM 32-bit architecture:
|
||||
* Architecture versions: v4, v5, v5te, v6, v6t2, v7.
|
||||
ARMv7 is the standard nowadays.
|
||||
* Instruction encoding: classic ARM or Thumb or Thumb-2.
|
||||
* Floating-point: software emulation, VFPv2, VFPv3-d16, VFP-v3.
|
||||
* ABI: the standard EABI (with floats passed in integer registers)
|
||||
or the EABI-HF variant (with floats passed in VFP registers).
|
||||
|
||||
Debian architecture names: `armel` and `armhf`.
|
||||
|
||||
# Reference documents
|
||||
|
||||
* Instruction set architecture:
|
||||
_ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition_.
|
||||
Alternatively:
|
||||
_ARM Architecture Reference Manual, ARMv8_, restricted to the AArch32 subset.
|
||||
* Application binary interface:
|
||||
_Procedure Call Standard for the ARM Architecture_
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Supported platforms
|
||||
|
||||
ARMv8 in 64-bit mode (AArch64).
|
||||
|
||||
Debian architecture name: `arm64`.
|
||||
|
||||
# Reference documents
|
||||
|
||||
* Instruction set architecture:
|
||||
_ARM Architecture Reference Manual, ARMv8_, restricted to the AArch64 subset.
|
||||
* Application binary interface:
|
||||
_Procedure Call Standard for the ARM 64-bit Architecture (AArch64)_
|
|
@ -59,28 +59,29 @@ let (++) x f = f x
|
|||
let compile_fundecl (ppf : formatter) fd_cmm =
|
||||
Proc.init ();
|
||||
Reg.reset();
|
||||
let build = Compilenv.current_build () in
|
||||
fd_cmm
|
||||
++ Selection.fundecl
|
||||
++ Timings.(accumulate_time (Selection build)) Selection.fundecl
|
||||
++ pass_dump_if ppf dump_selection "After instruction selection"
|
||||
++ Comballoc.fundecl
|
||||
++ Timings.(accumulate_time (Comballoc build)) Comballoc.fundecl
|
||||
++ pass_dump_if ppf dump_combine "After allocation combining"
|
||||
++ CSE.fundecl
|
||||
++ Timings.(accumulate_time (CSE build)) CSE.fundecl
|
||||
++ pass_dump_if ppf dump_cse "After CSE"
|
||||
++ liveness ppf
|
||||
++ Deadcode.fundecl
|
||||
++ Timings.(accumulate_time (Liveness build)) (liveness ppf)
|
||||
++ Timings.(accumulate_time (Deadcode build)) Deadcode.fundecl
|
||||
++ pass_dump_if ppf dump_live "Liveness analysis"
|
||||
++ Spill.fundecl
|
||||
++ liveness ppf
|
||||
++ Timings.(accumulate_time (Spill build)) Spill.fundecl
|
||||
++ Timings.(accumulate_time (Liveness build)) (liveness ppf)
|
||||
++ pass_dump_if ppf dump_spill "After spilling"
|
||||
++ Split.fundecl
|
||||
++ Timings.(accumulate_time (Split build)) Split.fundecl
|
||||
++ pass_dump_if ppf dump_split "After live range splitting"
|
||||
++ liveness ppf
|
||||
++ regalloc ppf 1
|
||||
++ Linearize.fundecl
|
||||
++ Timings.(accumulate_time (Liveness build)) (liveness ppf)
|
||||
++ Timings.(accumulate_time (Regalloc build)) (regalloc ppf 1)
|
||||
++ Timings.(accumulate_time (Linearize build)) Linearize.fundecl
|
||||
++ pass_dump_linear_if ppf dump_linear "Linearized code"
|
||||
++ Scheduling.fundecl
|
||||
++ Timings.(accumulate_time (Scheduling build)) Scheduling.fundecl
|
||||
++ pass_dump_linear_if ppf dump_scheduling "After instruction scheduling"
|
||||
++ Emit.fundecl
|
||||
++ Timings.(accumulate_time (Emit build)) Emit.fundecl
|
||||
|
||||
let compile_phrase ppf p =
|
||||
if !dump_cmm then fprintf ppf "%a@." Printcmm.phrase p;
|
||||
|
@ -99,7 +100,7 @@ let compile_genfuns ppf f =
|
|||
| _ -> ())
|
||||
(Cmmgen.generic_functions true [Compilenv.current_unit_infos ()])
|
||||
|
||||
let compile_unit asm_filename keep_asm obj_filename gen =
|
||||
let compile_unit ~source_provenance asm_filename keep_asm obj_filename gen =
|
||||
let create_asm = keep_asm || not !Emitaux.binary_backend_available in
|
||||
Emitaux.create_asm_file := create_asm;
|
||||
try
|
||||
|
@ -112,19 +113,25 @@ let compile_unit asm_filename keep_asm obj_filename gen =
|
|||
if not keep_asm then remove_file asm_filename;
|
||||
raise exn
|
||||
end;
|
||||
if Proc.assemble_file asm_filename obj_filename <> 0
|
||||
let assemble_result =
|
||||
Timings.(time (Assemble source_provenance))
|
||||
(Proc.assemble_file asm_filename) obj_filename
|
||||
in
|
||||
if assemble_result <> 0
|
||||
then raise(Error(Assembler_error asm_filename));
|
||||
if create_asm && not keep_asm then remove_file asm_filename
|
||||
with exn ->
|
||||
remove_file obj_filename;
|
||||
raise exn
|
||||
|
||||
let gen_implementation ?toplevel ppf (size, lam) =
|
||||
let gen_implementation ?toplevel ~source_provenance ppf (size, lam) =
|
||||
Emit.begin_assembly ();
|
||||
Closure.intro size lam
|
||||
Timings.(time (Clambda source_provenance)) (Closure.intro size) lam
|
||||
++ clambda_dump_if ppf
|
||||
++ Cmmgen.compunit size
|
||||
++ List.iter (compile_phrase ppf) ++ (fun () -> ());
|
||||
++ Timings.(time (Cmm source_provenance)) (Cmmgen.compunit size)
|
||||
++ Timings.(time (Compile_phrases source_provenance))
|
||||
(List.iter (compile_phrase ppf))
|
||||
++ (fun () -> ());
|
||||
(match toplevel with None -> () | Some f -> compile_genfuns ppf f);
|
||||
|
||||
(* We add explicit references to external primitive symbols. This
|
||||
|
@ -140,14 +147,14 @@ let gen_implementation ?toplevel ppf (size, lam) =
|
|||
);
|
||||
Emit.end_assembly ()
|
||||
|
||||
let compile_implementation ?toplevel prefixname ppf (size, lam) =
|
||||
let compile_implementation ?toplevel ~source_provenance prefixname ppf (size, lam) =
|
||||
let asmfile =
|
||||
if !keep_asm_file || !Emitaux.binary_backend_available
|
||||
then prefixname ^ ext_asm
|
||||
else Filename.temp_file "camlasm" ext_asm
|
||||
in
|
||||
compile_unit asmfile !keep_asm_file (prefixname ^ ext_obj)
|
||||
(fun () -> gen_implementation ?toplevel ppf (size, lam))
|
||||
compile_unit ~source_provenance asmfile !keep_asm_file (prefixname ^ ext_obj)
|
||||
(fun () -> gen_implementation ?toplevel ~source_provenance ppf (size, lam))
|
||||
|
||||
(* Error report *)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
val compile_implementation :
|
||||
?toplevel:(string -> bool) ->
|
||||
source_provenance:Timings.source_provenance ->
|
||||
string -> Format.formatter -> int * Lambda.lambda -> unit
|
||||
val compile_phrase :
|
||||
Format.formatter -> Cmm.phrase -> unit
|
||||
|
@ -24,5 +25,6 @@ val report_error: Format.formatter -> error -> unit
|
|||
|
||||
|
||||
val compile_unit:
|
||||
source_provenance:Timings.source_provenance ->
|
||||
string(*asm file*) -> bool(*keep asm*) ->
|
||||
string(*obj file*) -> (unit -> unit) -> unit
|
||||
|
|
|
@ -203,7 +203,8 @@ let scan_file obj_name tolink = match read_file obj_name with
|
|||
let make_startup_file ppf units_list =
|
||||
let compile_phrase p = Asmgen.compile_phrase ppf p in
|
||||
Location.input_name := "caml_startup"; (* set name of "current" input *)
|
||||
Compilenv.reset "_startup"; (* set the name of the "current" compunit *)
|
||||
Compilenv.reset ~source_provenance:Timings.Startup "_startup";
|
||||
(* set the name of the "current" compunit *)
|
||||
Emit.begin_assembly ();
|
||||
let name_list =
|
||||
List.flatten (List.map (fun (info,_,_) -> info.ui_defines) units_list) in
|
||||
|
@ -236,7 +237,7 @@ let make_startup_file ppf units_list =
|
|||
let make_shared_startup_file ppf units =
|
||||
let compile_phrase p = Asmgen.compile_phrase ppf p in
|
||||
Location.input_name := "caml_startup";
|
||||
Compilenv.reset "_shared_startup";
|
||||
Compilenv.reset ~source_provenance:Timings.Startup "_shared_startup";
|
||||
Emit.begin_assembly ();
|
||||
List.iter compile_phrase
|
||||
(Cmmgen.generic_functions true (List.map fst units));
|
||||
|
@ -267,7 +268,7 @@ let link_shared ppf objfiles output_name =
|
|||
then output_name ^ ".startup" ^ ext_asm
|
||||
else Filename.temp_file "camlstartup" ext_asm in
|
||||
let startup_obj = output_name ^ ".startup" ^ ext_obj in
|
||||
Asmgen.compile_unit
|
||||
Asmgen.compile_unit ~source_provenance:Timings.Startup
|
||||
startup !Clflags.keep_startup_file startup_obj
|
||||
(fun () ->
|
||||
make_shared_startup_file ppf
|
||||
|
@ -326,7 +327,7 @@ let link ppf objfiles output_name =
|
|||
then output_name ^ ".startup" ^ ext_asm
|
||||
else Filename.temp_file "camlstartup" ext_asm in
|
||||
let startup_obj = Filename.temp_file "camlstartup" ext_obj in
|
||||
Asmgen.compile_unit
|
||||
Asmgen.compile_unit ~source_provenance:Timings.Startup
|
||||
startup !Clflags.keep_startup_file startup_obj
|
||||
(fun () -> make_startup_file ppf units_tolink);
|
||||
Misc.try_finally
|
||||
|
|
|
@ -91,7 +91,7 @@ let make_package_object ppf members targetobj targetname coercion =
|
|||
| PM_intf -> None
|
||||
| PM_impl _ -> Some(Ident.create_persistent m.pm_name))
|
||||
members in
|
||||
Asmgen.compile_implementation
|
||||
Asmgen.compile_implementation ~source_provenance:(Timings.Pack targetname)
|
||||
(chop_extension_if_any objtemp) ppf
|
||||
(Translmod.transl_store_package
|
||||
components (Ident.create_persistent targetname) coercion);
|
||||
|
@ -175,7 +175,8 @@ let package_files ppf initial_env files targetcmx =
|
|||
(* Set the name of the current "input" *)
|
||||
Location.input_name := targetcmx;
|
||||
(* Set the name of the current compunit *)
|
||||
Compilenv.reset ?packname:!Clflags.for_package targetname;
|
||||
Compilenv.reset ~source_provenance:(Timings.Pack targetname)
|
||||
?packname:!Clflags.for_package targetname;
|
||||
try
|
||||
let coercion =
|
||||
Typemod.package_units initial_env files targetcmi targetname in
|
||||
|
|
|
@ -0,0 +1,549 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
module Env : sig
|
||||
type t
|
||||
|
||||
val new_descr : t -> Export_info.descr -> Export_id.t
|
||||
val record_descr : t -> Export_id.t -> Export_info.descr -> unit
|
||||
val get_descr : t -> Export_info.approx -> Export_info.descr option
|
||||
|
||||
val add_approx : t -> Variable.t -> Export_info.approx -> t
|
||||
val add_approx_maps : t -> Export_info.approx Variable.Map.t list -> t
|
||||
val find_approx : t -> Variable.t -> Export_info.approx
|
||||
|
||||
val get_symbol_descr : t -> Symbol.t -> Export_info.descr option
|
||||
|
||||
val new_unit_descr : t -> Export_id.t
|
||||
|
||||
module Global : sig
|
||||
(* "Global" as in "without local variable bindings". *)
|
||||
type t
|
||||
|
||||
val create_empty : unit -> t
|
||||
|
||||
val add_symbol : t -> Symbol.t -> Export_id.t -> t
|
||||
val new_symbol : t -> Symbol.t -> Export_id.t * t
|
||||
|
||||
val symbol_to_export_id_map : t -> Export_id.t Symbol.Map.t
|
||||
val export_id_to_descr_map : t -> Export_info.descr Export_id.Map.t
|
||||
end
|
||||
|
||||
(** Creates a new environment, sharing the mapping from export IDs to
|
||||
export descriptions with the given global environment. *)
|
||||
val empty_of_global : Global.t -> t
|
||||
end = struct
|
||||
let fresh_id () = Export_id.create (Compilenv.current_unit ())
|
||||
|
||||
module Global = struct
|
||||
type t =
|
||||
{ sym : Export_id.t Symbol.Map.t;
|
||||
(* Note that [ex_table]s themselves are shared (hence [ref] and not
|
||||
[mutable]). *)
|
||||
ex_table : Export_info.descr Export_id.Map.t ref;
|
||||
}
|
||||
|
||||
let create_empty () =
|
||||
{ sym = Symbol.Map.empty;
|
||||
ex_table = ref Export_id.Map.empty;
|
||||
}
|
||||
|
||||
let add_symbol t sym export_id =
|
||||
if Symbol.Map.mem sym t.sym then begin
|
||||
Misc.fatal_errorf "Build_export_info.Env.Global.add_symbol: cannot \
|
||||
rebind symbol %a in environment"
|
||||
Symbol.print sym
|
||||
end;
|
||||
{ t with sym = Symbol.Map.add sym export_id t.sym }
|
||||
|
||||
let new_symbol t sym =
|
||||
let export_id = fresh_id () in
|
||||
export_id, add_symbol t sym export_id
|
||||
|
||||
let symbol_to_export_id_map t = t.sym
|
||||
let export_id_to_descr_map t = !(t.ex_table)
|
||||
end
|
||||
|
||||
(* CR-someday mshinwell: The half-mutable nature of [t] with sharing of
|
||||
the [ex_table] is kind of nasty. Consider making it immutable. *)
|
||||
type t =
|
||||
{ var : Export_info.approx Variable.Map.t;
|
||||
sym : Export_id.t Symbol.Map.t;
|
||||
ex_table : Export_info.descr Export_id.Map.t ref;
|
||||
}
|
||||
|
||||
let empty_of_global (env : Global.t) =
|
||||
{ var = Variable.Map.empty;
|
||||
sym = env.sym;
|
||||
ex_table = env.ex_table;
|
||||
}
|
||||
|
||||
let extern_id_descr export_id =
|
||||
let export = Compilenv.approx_env () in
|
||||
try Some (Export_info.find_description export export_id)
|
||||
with Not_found -> None
|
||||
|
||||
let extern_symbol_descr sym =
|
||||
if Compilenv.is_predefined_exception sym
|
||||
then None
|
||||
else
|
||||
let export = Compilenv.approx_for_global (Symbol.compilation_unit sym) in
|
||||
try
|
||||
let id = Symbol.Map.find sym export.symbol_id in
|
||||
let descr = Export_info.find_description export id in
|
||||
Some descr
|
||||
with
|
||||
| Not_found -> None
|
||||
|
||||
let get_id_descr t export_id =
|
||||
try Some (Export_id.Map.find export_id !(t.ex_table))
|
||||
with Not_found -> extern_id_descr export_id
|
||||
|
||||
let get_symbol_descr t sym =
|
||||
try
|
||||
let export_id = Symbol.Map.find sym t.sym in
|
||||
Some (Export_id.Map.find export_id !(t.ex_table))
|
||||
with
|
||||
| Not_found -> extern_symbol_descr sym
|
||||
|
||||
let get_descr t (approx : Export_info.approx) =
|
||||
match approx with
|
||||
| Value_unknown -> None
|
||||
| Value_id export_id -> get_id_descr t export_id
|
||||
| Value_symbol sym -> get_symbol_descr t sym
|
||||
|
||||
let record_descr t id (descr : Export_info.descr) =
|
||||
if Export_id.Map.mem id !(t.ex_table) then begin
|
||||
Misc.fatal_errorf "Build_export_info.Env.record_descr: cannot rebind \
|
||||
export ID %a in environment"
|
||||
Export_id.print id
|
||||
end;
|
||||
t.ex_table := Export_id.Map.add id descr !(t.ex_table)
|
||||
|
||||
let new_descr t (descr : Export_info.descr) =
|
||||
let id = fresh_id () in
|
||||
record_descr t id descr;
|
||||
id
|
||||
|
||||
let new_unit_descr t =
|
||||
new_descr t (Value_constptr 0)
|
||||
|
||||
let add_approx t var approx =
|
||||
if Variable.Map.mem var t.var then begin
|
||||
Misc.fatal_errorf "Build_export_info.Env.add_approx: cannot rebind \
|
||||
variable %a in environment"
|
||||
Variable.print var
|
||||
end;
|
||||
{ t with var = Variable.Map.add var approx t.var; }
|
||||
|
||||
let add_approx_map t vars_to_approxs =
|
||||
Variable.Map.fold (fun var approx t -> add_approx t var approx)
|
||||
vars_to_approxs
|
||||
t
|
||||
|
||||
let add_approx_maps t vars_to_approxs_list =
|
||||
List.fold_left add_approx_map t vars_to_approxs_list
|
||||
|
||||
let find_approx t var : Export_info.approx =
|
||||
try Variable.Map.find var t.var with
|
||||
| Not_found -> Value_unknown
|
||||
end
|
||||
|
||||
let descr_of_constant (c : Flambda.const) : Export_info.descr =
|
||||
match c with
|
||||
(* [Const_pointer] is an immediate value of a type whose values may be
|
||||
boxed (typically a variant type with both constant and non-constant
|
||||
constructors). *)
|
||||
| Int i -> Value_int i
|
||||
| Char c -> Value_char c
|
||||
| Const_pointer i -> Value_constptr i
|
||||
|
||||
let descr_of_allocated_constant (c : Allocated_const.t) : Export_info.descr =
|
||||
match c with
|
||||
| Float f -> Value_float f
|
||||
| Int32 i -> Value_boxed_int (Int32, i)
|
||||
| Int64 i -> Value_boxed_int (Int64, i)
|
||||
| Nativeint i -> Value_boxed_int (Nativeint, i)
|
||||
| String s ->
|
||||
let v_string : Export_info.value_string =
|
||||
{ size = String.length s; contents = Unknown_or_mutable; }
|
||||
in
|
||||
Value_string v_string
|
||||
| Immutable_string s ->
|
||||
let v_string : Export_info.value_string =
|
||||
{ size = String.length s; contents = Contents s; }
|
||||
in
|
||||
Value_string v_string
|
||||
| Immutable_float_array fs ->
|
||||
Value_float_array {
|
||||
contents = Contents (Array.map (fun x -> Some x) (Array.of_list fs));
|
||||
size = List.length fs;
|
||||
}
|
||||
| Float_array fs ->
|
||||
Value_float_array {
|
||||
contents = Unknown_or_mutable;
|
||||
size = List.length fs;
|
||||
}
|
||||
|
||||
let rec approx_of_expr (env : Env.t) (flam : Flambda.t) : Export_info.approx =
|
||||
match flam with
|
||||
| Var var -> Env.find_approx env var
|
||||
| Let { var; defining_expr; body; _ } ->
|
||||
let approx = descr_of_named env defining_expr in
|
||||
let env = Env.add_approx env var approx in
|
||||
approx_of_expr env body
|
||||
| Let_mutable (_mut_var, _var, body) ->
|
||||
approx_of_expr env body
|
||||
| Let_rec (defs, body) ->
|
||||
let env =
|
||||
List.fold_left (fun env (var, defining_expr) ->
|
||||
let approx = descr_of_named env defining_expr in
|
||||
Env.add_approx env var approx)
|
||||
env defs
|
||||
in
|
||||
approx_of_expr env body
|
||||
| Apply { func; kind; _ } ->
|
||||
begin match kind with
|
||||
| Indirect -> Value_unknown
|
||||
| Direct closure_id' ->
|
||||
match Env.get_descr env (Env.find_approx env func) with
|
||||
| Some (Value_closure
|
||||
{ closure_id; set_of_closures = { results; _ }; }) ->
|
||||
assert (Closure_id.equal closure_id closure_id');
|
||||
assert (Closure_id.Map.mem closure_id results);
|
||||
Closure_id.Map.find closure_id results
|
||||
| _ -> Value_unknown
|
||||
end
|
||||
| Assign _ -> Value_id (Env.new_unit_descr env)
|
||||
| For _ -> Value_id (Env.new_unit_descr env)
|
||||
| While _ -> Value_id (Env.new_unit_descr env)
|
||||
| Static_raise _ | Static_catch _ | Try_with _ | If_then_else _
|
||||
| Switch _ | String_switch _ | Send _ | Proved_unreachable ->
|
||||
Value_unknown
|
||||
|
||||
and descr_of_named (env : Env.t) (named : Flambda.named)
|
||||
: Export_info.approx =
|
||||
match named with
|
||||
| Expr expr -> approx_of_expr env expr
|
||||
| Symbol sym -> Value_symbol sym
|
||||
| Read_mutable _ -> Value_unknown
|
||||
| Read_symbol_field (sym, i) ->
|
||||
begin match Env.get_symbol_descr env sym with
|
||||
| Some (Value_block (_, fields)) when Array.length fields > i -> fields.(i)
|
||||
| _ -> Value_unknown
|
||||
end
|
||||
| Const const ->
|
||||
Value_id (Env.new_descr env (descr_of_constant const))
|
||||
| Allocated_const const ->
|
||||
Value_id (Env.new_descr env (descr_of_allocated_constant const))
|
||||
| Prim (Pmakeblock (tag, Immutable), args, _dbg) ->
|
||||
let approxs = List.map (Env.find_approx env) args in
|
||||
let descr : Export_info.descr =
|
||||
Value_block (Tag.create_exn tag, Array.of_list approxs)
|
||||
in
|
||||
Value_id (Env.new_descr env descr)
|
||||
| Prim (Pfield i, [arg], _) ->
|
||||
begin match Env.get_descr env (Env.find_approx env arg) with
|
||||
| Some (Value_block (_, fields)) when Array.length fields > i -> fields.(i)
|
||||
| _ -> Value_unknown
|
||||
end
|
||||
| Prim (Pgetglobal id, _, _) ->
|
||||
Value_symbol (Compilenv.symbol_for_global' id)
|
||||
| Prim _ -> Value_unknown
|
||||
| Set_of_closures set ->
|
||||
let descr : Export_info.descr =
|
||||
Value_set_of_closures (describe_set_of_closures env set)
|
||||
in
|
||||
Value_id (Env.new_descr env descr)
|
||||
| Project_closure { set_of_closures; closure_id; } ->
|
||||
begin match Env.get_descr env (Env.find_approx env set_of_closures) with
|
||||
| Some (Value_set_of_closures set_of_closures) ->
|
||||
if not (Closure_id.Map.mem closure_id set_of_closures.results) then begin
|
||||
Misc.fatal_errorf "Could not build export description for \
|
||||
[Project_closure]: closure ID %a not in set of closures"
|
||||
Closure_id.print closure_id
|
||||
end;
|
||||
let descr : Export_info.descr =
|
||||
Value_closure { closure_id = closure_id; set_of_closures; }
|
||||
in
|
||||
Value_id (Env.new_descr env descr)
|
||||
| _ ->
|
||||
(* CR pchambart: This should be [assert false], but currently there are a
|
||||
few cases where this is less precise than inline_and_simplify.
|
||||
mshinwell: Can you elaborate? *)
|
||||
Value_unknown
|
||||
end
|
||||
| Move_within_set_of_closures { closure; start_from; move_to; } ->
|
||||
begin match Env.get_descr env (Env.find_approx env closure) with
|
||||
| Some (Value_closure { set_of_closures; closure_id; }) ->
|
||||
assert (Closure_id.equal closure_id start_from);
|
||||
let descr : Export_info.descr =
|
||||
Value_closure { closure_id = move_to; set_of_closures; }
|
||||
in
|
||||
Value_id (Env.new_descr env descr)
|
||||
| _ -> Value_unknown
|
||||
end
|
||||
| Project_var { closure; closure_id = closure_id'; var; } ->
|
||||
begin match Env.get_descr env (Env.find_approx env closure) with
|
||||
| Some (Value_closure
|
||||
{ set_of_closures = { bound_vars; _ }; closure_id; }) ->
|
||||
assert (Closure_id.equal closure_id closure_id');
|
||||
if not (Var_within_closure.Map.mem var bound_vars) then begin
|
||||
Misc.fatal_errorf "Project_var from %a (closure ID %a) of \
|
||||
variable %a that is not bound by the closure. \
|
||||
Variables bound by the closure are: %a"
|
||||
Variable.print closure
|
||||
Closure_id.print closure_id
|
||||
Var_within_closure.print var
|
||||
(Var_within_closure.Map.print (fun _ _ -> ())) bound_vars
|
||||
end;
|
||||
Var_within_closure.Map.find var bound_vars
|
||||
| _ -> Value_unknown
|
||||
end
|
||||
|
||||
and describe_set_of_closures env (set : Flambda.set_of_closures)
|
||||
: Export_info.value_set_of_closures =
|
||||
let bound_vars_approx =
|
||||
Variable.Map.map (Env.find_approx env) set.free_vars
|
||||
in
|
||||
let specialised_args_approx =
|
||||
Variable.Map.map (Env.find_approx env) set.specialised_args
|
||||
in
|
||||
let closures_approx =
|
||||
(* To build an approximation of the results, we need an
|
||||
approximation of the functions. The first one we can build is
|
||||
one where every function returns something unknown.
|
||||
*)
|
||||
(* CR-someday pchambart: we could improve a bit on that by building a
|
||||
recursive approximation of the closures: The value_closure
|
||||
description contains a [value_set_of_closures]. We could replace
|
||||
this field by a [Expr_id.t] or an [approx].
|
||||
mshinwell: Deferred for now.
|
||||
*)
|
||||
let initial_value_set_of_closures =
|
||||
{ Export_info.
|
||||
set_of_closures_id = set.function_decls.set_of_closures_id;
|
||||
bound_vars = Var_within_closure.wrap_map bound_vars_approx;
|
||||
results =
|
||||
Closure_id.wrap_map
|
||||
(Variable.Map.map (fun _ -> Export_info.Value_unknown)
|
||||
set.function_decls.funs);
|
||||
aliased_symbol = None;
|
||||
}
|
||||
in
|
||||
Variable.Map.mapi (fun fun_var _function_decl ->
|
||||
let descr : Export_info.descr =
|
||||
Value_closure
|
||||
{ closure_id = Closure_id.wrap fun_var;
|
||||
set_of_closures = initial_value_set_of_closures;
|
||||
}
|
||||
in
|
||||
Export_info.Value_id (Env.new_descr env descr))
|
||||
set.function_decls.funs
|
||||
in
|
||||
let closure_env =
|
||||
Env.add_approx_maps env
|
||||
[closures_approx; bound_vars_approx; specialised_args_approx]
|
||||
in
|
||||
let results =
|
||||
let result_approx _var (function_decl : Flambda.function_declaration) =
|
||||
approx_of_expr closure_env function_decl.body
|
||||
in
|
||||
Variable.Map.mapi result_approx set.function_decls.funs
|
||||
in
|
||||
{ set_of_closures_id = set.function_decls.set_of_closures_id;
|
||||
bound_vars = Var_within_closure.wrap_map bound_vars_approx;
|
||||
results = Closure_id.wrap_map results;
|
||||
aliased_symbol = None;
|
||||
}
|
||||
|
||||
let approx_of_constant_defining_value_block_field env
|
||||
(c : Flambda.constant_defining_value_block_field) : Export_info.approx =
|
||||
match c with
|
||||
| Symbol s -> Value_symbol s
|
||||
| Const c -> Value_id (Env.new_descr env (descr_of_constant c))
|
||||
|
||||
let describe_constant_defining_value env export_id symbol
|
||||
(const : Flambda.constant_defining_value) =
|
||||
let env =
|
||||
(* Assignments of variables to export IDs are local to each constant
|
||||
defining value. *)
|
||||
Env.empty_of_global env
|
||||
in
|
||||
match const with
|
||||
| Allocated_const alloc_const ->
|
||||
let descr = descr_of_allocated_constant alloc_const in
|
||||
Env.record_descr env export_id descr
|
||||
| Block (tag, fields) ->
|
||||
let approxs =
|
||||
List.map (approx_of_constant_defining_value_block_field env) fields
|
||||
in
|
||||
Env.record_descr env export_id (Value_block (tag, Array.of_list approxs))
|
||||
| Set_of_closures set_of_closures ->
|
||||
let descr : Export_info.descr =
|
||||
Value_set_of_closures
|
||||
{ (describe_set_of_closures env set_of_closures) with
|
||||
aliased_symbol = Some symbol;
|
||||
}
|
||||
in
|
||||
Env.record_descr env export_id descr
|
||||
| Project_closure (sym, closure_id) ->
|
||||
begin match Env.get_symbol_descr env sym with
|
||||
| Some (Value_set_of_closures set_of_closures) ->
|
||||
if not (Closure_id.Map.mem closure_id set_of_closures.results) then begin
|
||||
Misc.fatal_errorf "Could not build export description for \
|
||||
[Project_closure] constant defining value: closure ID %a not in \
|
||||
set of closures"
|
||||
Closure_id.print closure_id
|
||||
end;
|
||||
let descr =
|
||||
Export_info.Value_closure
|
||||
{ closure_id = closure_id; set_of_closures; }
|
||||
in
|
||||
Env.record_descr env export_id descr
|
||||
| None ->
|
||||
Misc.fatal_errorf
|
||||
"Cannot project symbol %a to closure_id %a. \
|
||||
No available export description@."
|
||||
Symbol.print sym
|
||||
Closure_id.print closure_id
|
||||
| Some (Value_closure _) ->
|
||||
Misc.fatal_errorf
|
||||
"Cannot project symbol %a to closure_id %a. \
|
||||
The symbol is a closure instead of a set of closures.@."
|
||||
Symbol.print sym
|
||||
Closure_id.print closure_id
|
||||
| Some _ ->
|
||||
Misc.fatal_errorf
|
||||
"Cannot project symbol %a to closure_id %a. \
|
||||
The symbol is not a set of closures.@."
|
||||
Symbol.print sym
|
||||
Closure_id.print closure_id
|
||||
end
|
||||
|
||||
let describe_program (env : Env.Global.t) (program : Flambda.program) =
|
||||
let rec loop env (program : Flambda.program_body) =
|
||||
match program with
|
||||
| Let_symbol (symbol, constant_defining_value, program) ->
|
||||
let id, env = Env.Global.new_symbol env symbol in
|
||||
describe_constant_defining_value env id symbol constant_defining_value;
|
||||
loop env program
|
||||
| Let_rec_symbol (defs, program) ->
|
||||
let env, defs =
|
||||
List.fold_left (fun (env, defs) (symbol, def) ->
|
||||
let id, env = Env.Global.new_symbol env symbol in
|
||||
env, ((id, symbol, def) :: defs))
|
||||
(env, []) defs
|
||||
in
|
||||
(* [Project_closure]s are separated to be handled last. They are the
|
||||
only values that need a description for their argument. *)
|
||||
let project_closures, other_constants =
|
||||
List.partition (function
|
||||
| _, _, Flambda.Project_closure _ -> true
|
||||
| _ -> false)
|
||||
defs
|
||||
in
|
||||
List.iter (fun (id, symbol, def) ->
|
||||
describe_constant_defining_value env id symbol def)
|
||||
other_constants;
|
||||
List.iter (fun (id, symbol, def) ->
|
||||
describe_constant_defining_value env id symbol def)
|
||||
project_closures;
|
||||
loop env program
|
||||
| Initialize_symbol (symbol, tag, fields, program) ->
|
||||
let id =
|
||||
let env =
|
||||
(* Assignments of variables to export IDs are local to each
|
||||
[Initialize_symbol] construction. *)
|
||||
Env.empty_of_global env
|
||||
in
|
||||
let field_approxs = List.map (approx_of_expr env) fields in
|
||||
let descr : Export_info.descr =
|
||||
Value_block (tag, Array.of_list field_approxs)
|
||||
in
|
||||
Env.new_descr env descr
|
||||
in
|
||||
let env = Env.Global.add_symbol env symbol id in
|
||||
loop env program
|
||||
| Effect (_expr, program) -> loop env program
|
||||
| End symbol -> symbol, env
|
||||
in
|
||||
loop env program.program_body
|
||||
|
||||
let build_export_info ~(backend : (module Backend_intf.S))
|
||||
(program : Flambda.program) : Export_info.t =
|
||||
if !Clflags.opaque then
|
||||
Export_info.empty
|
||||
else
|
||||
(* CR pchambart: Should probably use that instead of the ident of
|
||||
the module as global identifier.
|
||||
mshinwell: Is "that" the variable "_global_symbol"? *)
|
||||
let _global_symbol, env =
|
||||
describe_program (Env.Global.create_empty ()) program
|
||||
in
|
||||
let globals =
|
||||
let root_approx : Export_info.approx =
|
||||
Value_symbol (Compilenv.current_unit_symbol ())
|
||||
in
|
||||
Ident.Map.singleton (Compilenv.current_unit_id ()) root_approx
|
||||
in
|
||||
let sets_of_closures =
|
||||
Flambda_utils.all_function_decls_indexed_by_set_of_closures_id program
|
||||
in
|
||||
let closures =
|
||||
Flambda_utils.all_function_decls_indexed_by_closure_id program
|
||||
in
|
||||
let invariant_params =
|
||||
Set_of_closures_id.Map.map
|
||||
(fun { Flambda. function_decls; _ } ->
|
||||
Invariant_params.invariant_params_in_recursion
|
||||
~backend function_decls)
|
||||
(Flambda_utils.all_sets_of_closures_map program)
|
||||
in
|
||||
let unnested_values =
|
||||
Env.Global.export_id_to_descr_map env
|
||||
in
|
||||
let invariant_params =
|
||||
let export = Compilenv.approx_env () in
|
||||
Export_id.Map.fold (fun _eid (descr:Export_info.descr)
|
||||
(invariant_params) ->
|
||||
match descr with
|
||||
| Value_closure { set_of_closures }
|
||||
| Value_set_of_closures set_of_closures ->
|
||||
let { Export_info.set_of_closures_id } = set_of_closures in
|
||||
begin match
|
||||
Set_of_closures_id.Map.find set_of_closures_id
|
||||
export.invariant_params
|
||||
with
|
||||
| exception Not_found ->
|
||||
invariant_params
|
||||
| (set:Variable.Set.t Variable.Map.t) ->
|
||||
Set_of_closures_id.Map.add set_of_closures_id set invariant_params
|
||||
end
|
||||
| _ ->
|
||||
invariant_params)
|
||||
unnested_values invariant_params
|
||||
in
|
||||
let values =
|
||||
Export_info.nest_eid_map unnested_values
|
||||
in
|
||||
Export_info.create ~values ~globals
|
||||
~symbol_id:(Env.Global.symbol_to_export_id_map env)
|
||||
~offset_fun:Closure_id.Map.empty
|
||||
~offset_fv:Var_within_closure.Map.empty
|
||||
~sets_of_closures ~closures
|
||||
~constant_sets_of_closures:Set_of_closures_id.Set.empty
|
||||
~invariant_params
|
|
@ -0,0 +1,23 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(** Construct export information, for emission into .cmx files, from an
|
||||
Flambda program. *)
|
||||
|
||||
val build_export_info :
|
||||
backend:(module Backend_intf.S) ->
|
||||
Flambda.program ->
|
||||
Export_info.t
|
|
@ -26,13 +26,14 @@ type ustructured_constant =
|
|||
| Uconst_block of int * uconstant list
|
||||
| Uconst_float_array of float list
|
||||
| Uconst_string of string
|
||||
| Uconst_closure of ufunction list * string * uconstant list
|
||||
|
||||
and uconstant =
|
||||
| Uconst_ref of string * ustructured_constant
|
||||
| Uconst_ref of string * ustructured_constant option
|
||||
| Uconst_int of int
|
||||
| Uconst_ptr of int
|
||||
|
||||
type ulambda =
|
||||
and ulambda =
|
||||
Uvar of Ident.t
|
||||
| Uconst of uconstant
|
||||
| Udirect_apply of function_label * ulambda list * Debuginfo.t
|
||||
|
@ -53,6 +54,7 @@ type ulambda =
|
|||
| Ufor of Ident.t * ulambda * ulambda * direction_flag * ulambda
|
||||
| Uassign of Ident.t * ulambda
|
||||
| Usend of meth_kind * ulambda * ulambda * ulambda list * Debuginfo.t
|
||||
| Uunreachable
|
||||
|
||||
and ufunction = {
|
||||
label : function_label;
|
||||
|
@ -133,6 +135,7 @@ let rank_structured_constant = function
|
|||
| Uconst_block _ -> 4
|
||||
| Uconst_float_array _ -> 5
|
||||
| Uconst_string _ -> 6
|
||||
| Uconst_closure _ -> 7
|
||||
|
||||
let compare_structured_constants c1 c2 =
|
||||
match c1, c2 with
|
||||
|
@ -146,5 +149,8 @@ let compare_structured_constants c1 c2 =
|
|||
| Uconst_float_array l1, Uconst_float_array l2 ->
|
||||
compare_float_lists l1 l2
|
||||
| Uconst_string s1, Uconst_string s2 -> String.compare s1 s2
|
||||
| _, _ -> rank_structured_constant c1 - rank_structured_constant c2
|
||||
(* no overflow possible here *)
|
||||
| Uconst_closure (_,lbl1,_), Uconst_closure (_,lbl2,_) ->
|
||||
String.compare lbl1 lbl2
|
||||
| _, _ ->
|
||||
(* no overflow possible here *)
|
||||
rank_structured_constant c1 - rank_structured_constant c2
|
||||
|
|
|
@ -26,13 +26,14 @@ type ustructured_constant =
|
|||
| Uconst_block of int * uconstant list
|
||||
| Uconst_float_array of float list
|
||||
| Uconst_string of string
|
||||
| Uconst_closure of ufunction list * string * uconstant list
|
||||
|
||||
and uconstant =
|
||||
| Uconst_ref of string * ustructured_constant
|
||||
| Uconst_ref of string * ustructured_constant option
|
||||
| Uconst_int of int
|
||||
| Uconst_ptr of int
|
||||
|
||||
type ulambda =
|
||||
and ulambda =
|
||||
Uvar of Ident.t
|
||||
| Uconst of uconstant
|
||||
| Udirect_apply of function_label * ulambda list * Debuginfo.t
|
||||
|
@ -53,6 +54,7 @@ type ulambda =
|
|||
| Ufor of Ident.t * ulambda * ulambda * direction_flag * ulambda
|
||||
| Uassign of Ident.t * ulambda
|
||||
| Usend of meth_kind * ulambda * ulambda * ulambda list * Debuginfo.t
|
||||
| Uunreachable
|
||||
|
||||
and ufunction = {
|
||||
label : function_label;
|
||||
|
|
|
@ -83,6 +83,7 @@ let occurs_var var u =
|
|||
| Uassign(id, u) -> id = var || occurs u
|
||||
| Usend(_, met, obj, args, _) ->
|
||||
occurs met || occurs obj || List.exists occurs args
|
||||
| Uunreachable -> false
|
||||
and occurs_array a =
|
||||
try
|
||||
for i = 0 to Array.length a - 1 do
|
||||
|
@ -235,6 +236,7 @@ let lambda_smaller lam threshold =
|
|||
| Usend(_, met, obj, args, _) ->
|
||||
size := !size + 8;
|
||||
lambda_size met; lambda_size obj; lambda_list_size args
|
||||
| Uunreachable -> ()
|
||||
and lambda_list_size l = List.iter lambda_size l
|
||||
and lambda_array_size a = Array.iter lambda_size a in
|
||||
try
|
||||
|
@ -258,7 +260,7 @@ let rec is_pure_clambda = function
|
|||
|
||||
let make_const c = (Uconst c, Value_const c)
|
||||
let make_const_ref c =
|
||||
make_const(Uconst_ref(Compilenv.new_structured_constant ~shared:true c, c))
|
||||
make_const(Uconst_ref(Compilenv.new_structured_constant ~shared:true c, Some c))
|
||||
let make_const_int n = make_const (Uconst_int n)
|
||||
let make_const_ptr n = make_const (Uconst_ptr n)
|
||||
let make_const_bool b = make_const_ptr(if b then 1 else 0)
|
||||
|
@ -320,7 +322,7 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* float *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_float n1))] when fpc ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_float n1)))] when fpc ->
|
||||
begin match p with
|
||||
| Pintoffloat -> make_const_int (int_of_float n1)
|
||||
| Pnegfloat -> make_const_float (-. n1)
|
||||
|
@ -328,8 +330,8 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* float, float *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_float n1));
|
||||
Value_const(Uconst_ref(_, Uconst_float n2))] when fpc ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_float n1)));
|
||||
Value_const(Uconst_ref(_, Some (Uconst_float n2)))] when fpc ->
|
||||
begin match p with
|
||||
| Paddfloat -> make_const_float (n1 +. n2)
|
||||
| Psubfloat -> make_const_float (n1 -. n2)
|
||||
|
@ -339,7 +341,7 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* nativeint *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_nativeint n))] ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_nativeint n)))] ->
|
||||
begin match p with
|
||||
| Pintofbint Pnativeint -> make_const_int (Nativeint.to_int n)
|
||||
| Pcvtbint(Pnativeint, Pint32) -> make_const_int32 (Nativeint.to_int32 n)
|
||||
|
@ -348,8 +350,8 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* nativeint, nativeint *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_nativeint n1));
|
||||
Value_const(Uconst_ref(_, Uconst_nativeint n2))] ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_nativeint n1)));
|
||||
Value_const(Uconst_ref(_, Some (Uconst_nativeint n2)))] ->
|
||||
begin match p with
|
||||
| Paddbint Pnativeint -> make_const_natint (Nativeint.add n1 n2)
|
||||
| Psubbint Pnativeint -> make_const_natint (Nativeint.sub n1 n2)
|
||||
|
@ -365,7 +367,7 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* nativeint, int *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_nativeint n1));
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_nativeint n1)));
|
||||
Value_const(Uconst_int n2)] ->
|
||||
begin match p with
|
||||
| Plslbint Pnativeint when 0 <= n2 && n2 < 8 * Arch.size_int ->
|
||||
|
@ -377,7 +379,7 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* int32 *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_int32 n))] ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_int32 n)))] ->
|
||||
begin match p with
|
||||
| Pintofbint Pint32 -> make_const_int (Int32.to_int n)
|
||||
| Pcvtbint(Pint32, Pnativeint) -> make_const_natint (Nativeint.of_int32 n)
|
||||
|
@ -386,8 +388,8 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* int32, int32 *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_int32 n1));
|
||||
Value_const(Uconst_ref(_, Uconst_int32 n2))] ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_int32 n1)));
|
||||
Value_const(Uconst_ref(_, Some (Uconst_int32 n2)))] ->
|
||||
begin match p with
|
||||
| Paddbint Pint32 -> make_const_int32 (Int32.add n1 n2)
|
||||
| Psubbint Pint32 -> make_const_int32 (Int32.sub n1 n2)
|
||||
|
@ -401,7 +403,7 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* int32, int *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_int32 n1));
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_int32 n1)));
|
||||
Value_const(Uconst_int n2)] ->
|
||||
begin match p with
|
||||
| Plslbint Pint32 when 0 <= n2 && n2 < 32 ->
|
||||
|
@ -413,7 +415,7 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* int64 *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_int64 n))] ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_int64 n)))] ->
|
||||
begin match p with
|
||||
| Pintofbint Pint64 -> make_const_int (Int64.to_int n)
|
||||
| Pcvtbint(Pint64, Pint32) -> make_const_int32 (Int64.to_int32 n)
|
||||
|
@ -422,8 +424,8 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* int64, int64 *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_int64 n1));
|
||||
Value_const(Uconst_ref(_, Uconst_int64 n2))] ->
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_int64 n1)));
|
||||
Value_const(Uconst_ref(_, Some (Uconst_int64 n2)))] ->
|
||||
begin match p with
|
||||
| Paddbint Pint64 -> make_const_int64 (Int64.add n1 n2)
|
||||
| Psubbint Pint64 -> make_const_int64 (Int64.sub n1 n2)
|
||||
|
@ -437,7 +439,7 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
| _ -> default
|
||||
end
|
||||
(* int64, int *)
|
||||
| [Value_const(Uconst_ref(_, Uconst_int64 n1));
|
||||
| [Value_const(Uconst_ref(_, Some (Uconst_int64 n1)));
|
||||
Value_const(Uconst_int n2)] ->
|
||||
begin match p with
|
||||
| Plslbint Pint64 when 0 <= n2 && n2 < 64 ->
|
||||
|
@ -455,7 +457,8 @@ let simplif_arith_prim_pure fpc p (args, approxs) dbg =
|
|||
|
||||
let field_approx n = function
|
||||
| Value_tuple a when n < Array.length a -> a.(n)
|
||||
| Value_const (Uconst_ref(_, Uconst_block(_, l))) when n < List.length l ->
|
||||
| Value_const (Uconst_ref(_, Some (Uconst_block(_, l))))
|
||||
when n < List.length l ->
|
||||
Value_const (List.nth l n)
|
||||
| _ -> Value_unknown
|
||||
|
||||
|
@ -472,19 +475,19 @@ let simplif_prim_pure fpc p (args, approxs) dbg =
|
|||
let name =
|
||||
Compilenv.new_structured_constant cst ~shared:true
|
||||
in
|
||||
make_const (Uconst_ref (name, cst))
|
||||
make_const (Uconst_ref (name, Some cst))
|
||||
with Exit ->
|
||||
(Uprim(p, args, dbg), Value_tuple (Array.of_list approxs))
|
||||
end
|
||||
(* Field access *)
|
||||
| Pfield n, _, [ Value_const(Uconst_ref(_, Uconst_block(_, l))) ]
|
||||
| Pfield n, _, [ Value_const(Uconst_ref(_, Some (Uconst_block(_, l)))) ]
|
||||
when n < List.length l ->
|
||||
make_const (List.nth l n)
|
||||
| Pfield n, [ Uprim(Pmakeblock _, ul, _) ], [approx]
|
||||
when n < List.length ul ->
|
||||
(List.nth ul n, field_approx n approx)
|
||||
(* Strings *)
|
||||
| Pstringlength, _, [ Value_const(Uconst_ref(_, Uconst_string s)) ] ->
|
||||
| Pstringlength, _, [ Value_const(Uconst_ref(_, Some (Uconst_string s))) ] ->
|
||||
make_const_int (String.length s)
|
||||
(* Identity *)
|
||||
| Pidentity, [arg1], [app1] ->
|
||||
|
@ -602,7 +605,7 @@ let rec substitute fpc sb ulam =
|
|||
in this substitute function.
|
||||
*)
|
||||
match sarg with
|
||||
| Uconst (Uconst_ref (_, Uconst_block (tag, _))) ->
|
||||
| Uconst (Uconst_ref (_, Some (Uconst_block (tag, _)))) ->
|
||||
find_action sw.us_index_blocks sw.us_actions_blocks tag
|
||||
| Uconst (Uconst_ptr tag) ->
|
||||
find_action sw.us_index_consts sw.us_actions_consts tag
|
||||
|
@ -665,6 +668,8 @@ let rec substitute fpc sb ulam =
|
|||
| Usend(k, u1, u2, ul, dbg) ->
|
||||
Usend(k, substitute fpc sb u1, substitute fpc sb u2,
|
||||
List.map (substitute fpc sb) ul, dbg)
|
||||
| Uunreachable ->
|
||||
Uunreachable
|
||||
|
||||
(* Perform an inline expansion *)
|
||||
|
||||
|
@ -838,7 +843,7 @@ let rec close fenv cenv = function
|
|||
let name =
|
||||
Compilenv.new_structured_constant cst ~shared
|
||||
in
|
||||
Uconst_ref (name, cst)
|
||||
Uconst_ref (name, Some cst)
|
||||
in
|
||||
let rec transl = function
|
||||
| Const_base(Const_int n) -> Uconst_int n
|
||||
|
@ -1313,15 +1318,17 @@ let collect_exported_structured_constants a =
|
|||
| Value_const c -> const c
|
||||
| Value_unknown | Value_global_field _ -> ()
|
||||
and const = function
|
||||
| Uconst_ref (s, c) ->
|
||||
| Uconst_ref (s, (Some c)) ->
|
||||
Compilenv.add_exported_constant s;
|
||||
structured_constant c
|
||||
| Uconst_ref (s, None) -> assert false (* Cannot be generated *)
|
||||
| Uconst_int _ | Uconst_ptr _ -> ()
|
||||
and structured_constant = function
|
||||
| Uconst_block (_, ul) -> List.iter const ul
|
||||
| Uconst_float _ | Uconst_int32 _
|
||||
| Uconst_int64 _ | Uconst_nativeint _
|
||||
| Uconst_float_array _ | Uconst_string _ -> ()
|
||||
| Uconst_closure _ -> assert false (* Cannot be generated *)
|
||||
and ulam = function
|
||||
| Uvar _ -> ()
|
||||
| Uconst c -> const c
|
||||
|
@ -1351,6 +1358,7 @@ let collect_exported_structured_constants a =
|
|||
| Ufor (_, u1, u2, _, u3) -> ulam u1; ulam u2; ulam u3
|
||||
| Uassign (_, u) -> ulam u
|
||||
| Usend (_, u1, u2, ul, _) -> ulam u1; ulam u2; List.iter ulam ul
|
||||
| Uunreachable -> ()
|
||||
in
|
||||
approx a
|
||||
|
||||
|
@ -1366,7 +1374,11 @@ let intro size lam =
|
|||
global_approx := Array.init size (fun i -> Value_global_field (id, i));
|
||||
Compilenv.set_global_approx(Value_tuple !global_approx);
|
||||
let (ulam, approx) = close Tbl.empty Tbl.empty lam in
|
||||
if !Clflags.opaque
|
||||
let opaque =
|
||||
!Clflags.opaque
|
||||
|| Env.is_imported_opaque (Compilenv.current_unit_name ())
|
||||
in
|
||||
if opaque
|
||||
then Compilenv.set_global_approx(Value_unknown)
|
||||
else collect_exported_structured_constants (Value_tuple !global_approx);
|
||||
global_approx := [||];
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
type result = {
|
||||
function_offsets : int Closure_id.Map.t;
|
||||
free_variable_offsets : int Var_within_closure.Map.t;
|
||||
}
|
||||
|
||||
let add_closure_offsets
|
||||
{ function_offsets; free_variable_offsets }
|
||||
({ function_decls; free_vars } : Flambda.set_of_closures) =
|
||||
(* Build the table mapping the functions declared by the set of closures
|
||||
to the positions of their individual "infix" closures inside the runtime
|
||||
closure block. (All of the environment entries will come afterwards.) *)
|
||||
let assign_function_offset id function_decl (map, env_pos) =
|
||||
let pos = env_pos + 1 in
|
||||
let env_pos =
|
||||
let arity = Flambda_utils.function_arity function_decl in
|
||||
env_pos
|
||||
+ 1 (* GC header; either [Closure_tag] or [Infix_tag] *)
|
||||
+ 1 (* full application code pointer *)
|
||||
+ 1 (* arity *)
|
||||
+ (if arity > 1 then 1 else 0) (* partial application code pointer *)
|
||||
in
|
||||
let closure_id = Closure_id.wrap id in
|
||||
if Closure_id.Map.mem closure_id map then begin
|
||||
Misc.fatal_errorf "Closure_offsets.add_closure_offsets: function \
|
||||
offset for %a would be defined multiple times"
|
||||
Closure_id.print closure_id
|
||||
end;
|
||||
let map = Closure_id.Map.add closure_id pos map in
|
||||
(map, env_pos)
|
||||
in
|
||||
let function_offsets, free_variable_pos =
|
||||
Variable.Map.fold assign_function_offset
|
||||
function_decls.funs (function_offsets, -1)
|
||||
in
|
||||
(* Adds the mapping of free variables to their offset. Recall that
|
||||
projections of [Var_within_closure]s are only currently used when
|
||||
compiling accesses to the closure of a function from outside that
|
||||
function (in particular, as a result of inlining). Accesses to
|
||||
a function's own closure are compiled directly via normal [Var]
|
||||
accesses. *)
|
||||
(* CR-someday mshinwell: As discussed with lwhite, maybe this isn't
|
||||
ideal, and the self accesses should be explicitly marked too. *)
|
||||
let assign_free_variable_offset var _ (map, pos) =
|
||||
let var_within_closure = Var_within_closure.wrap var in
|
||||
if Var_within_closure.Map.mem var_within_closure map then begin
|
||||
Misc.fatal_errorf "Closure_offsets.add_closure_offsets: free variable \
|
||||
offset for %a would be defined multiple times"
|
||||
Var_within_closure.print var_within_closure
|
||||
end;
|
||||
let map = Var_within_closure.Map.add var_within_closure pos map in
|
||||
(map, pos + 1)
|
||||
in
|
||||
let free_variable_offsets, _ =
|
||||
Variable.Map.fold assign_free_variable_offset
|
||||
free_vars (free_variable_offsets, free_variable_pos)
|
||||
in
|
||||
{ function_offsets;
|
||||
free_variable_offsets;
|
||||
}
|
||||
|
||||
let compute (program:Flambda.program) =
|
||||
let init : result =
|
||||
{ function_offsets = Closure_id.Map.empty;
|
||||
free_variable_offsets = Var_within_closure.Map.empty;
|
||||
}
|
||||
in
|
||||
let r =
|
||||
List.fold_left add_closure_offsets
|
||||
init (Flambda_utils.all_sets_of_closures program)
|
||||
in
|
||||
r
|
||||
|
||||
let compute_reexported_offsets program
|
||||
~current_unit_offset_fun ~current_unit_offset_fv
|
||||
~imported_units_offset_fun ~imported_units_offset_fv =
|
||||
let offset_fun = ref current_unit_offset_fun in
|
||||
let offset_fv = ref current_unit_offset_fv in
|
||||
let used_closure_id closure_id =
|
||||
match Closure_id.Map.find closure_id imported_units_offset_fun with
|
||||
| offset ->
|
||||
assert (not (Closure_id.Map.mem closure_id current_unit_offset_fun));
|
||||
begin match Closure_id.Map.find closure_id !offset_fun with
|
||||
| exception Not_found ->
|
||||
offset_fun := Closure_id.Map.add closure_id offset !offset_fun
|
||||
| offset' -> assert (offset = offset')
|
||||
end
|
||||
| exception Not_found ->
|
||||
assert (Closure_id.Map.mem closure_id current_unit_offset_fun)
|
||||
in
|
||||
let used_var_within_closure var =
|
||||
match Var_within_closure.Map.find var imported_units_offset_fv with
|
||||
| offset ->
|
||||
assert (not (Var_within_closure.Map.mem var current_unit_offset_fv));
|
||||
begin match Var_within_closure.Map.find var !offset_fv with
|
||||
| exception Not_found ->
|
||||
offset_fv := Var_within_closure.Map.add var offset !offset_fv
|
||||
| offset' -> assert (offset = offset')
|
||||
end
|
||||
| exception Not_found ->
|
||||
assert (Var_within_closure.Map.mem var current_unit_offset_fv)
|
||||
in
|
||||
Flambda_iterators.iter_named_of_program program
|
||||
~f:(fun (named : Flambda.named) ->
|
||||
match named with
|
||||
| Project_closure { closure_id; _ } ->
|
||||
used_closure_id closure_id
|
||||
| Move_within_set_of_closures { start_from; move_to; _ } ->
|
||||
used_closure_id start_from;
|
||||
used_closure_id move_to
|
||||
| Project_var { closure_id; var; _ } ->
|
||||
used_closure_id closure_id;
|
||||
used_var_within_closure var
|
||||
| Symbol _ | Const _ | Allocated_const _ | Read_mutable _
|
||||
| Read_symbol_field _ | Set_of_closures _ | Prim _ | Expr _ -> ());
|
||||
Flambda_iterators.iter_constant_defining_values_on_program program
|
||||
~f:(fun (const : Flambda.constant_defining_value) ->
|
||||
match const with
|
||||
| Project_closure (_, closure_id) -> used_closure_id closure_id
|
||||
| Allocated_const _ | Block _ | Set_of_closures _ -> ());
|
||||
!offset_fun, !offset_fv
|
|
@ -0,0 +1,42 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(** Assign numerical offsets, within closure blocks, for code pointers and
|
||||
environment entries. *)
|
||||
|
||||
type result = private {
|
||||
function_offsets : int Closure_id.Map.t;
|
||||
free_variable_offsets : int Var_within_closure.Map.t;
|
||||
}
|
||||
|
||||
val compute : Flambda.program -> result
|
||||
|
||||
(** If compilation unit [C] references [B], which contains functions inlined
|
||||
from another compilation unit [A], then we may need to know the layout of
|
||||
closures inside (or constructed by code inside) a.cmx in order to
|
||||
compile c.cmx. Unfortunately a.cmx is permitted to be absent during such
|
||||
compilation; c.cmx will be compiled using just b.cmx. As such, when
|
||||
building the .cmx export information for a given compilation unit, we
|
||||
also include information about the layout of any closures that it depends
|
||||
on from other compilation units. This means that when situations as just
|
||||
describe arise, we always have access to the necessary closure offsets. *)
|
||||
val compute_reexported_offsets
|
||||
: Flambda.program
|
||||
-> current_unit_offset_fun:int Closure_id.Map.t
|
||||
-> current_unit_offset_fv:int Var_within_closure.Map.t
|
||||
-> imported_units_offset_fun:int Closure_id.Map.t
|
||||
-> imported_units_offset_fv:int Var_within_closure.Map.t
|
||||
-> int Closure_id.Map.t * int Var_within_closure.Map.t
|
|
@ -727,7 +727,7 @@ let transl_structured_constant cst =
|
|||
(* Translate constant closures *)
|
||||
|
||||
let constant_closures =
|
||||
ref ([] : (string * ufunction list) list)
|
||||
ref ([] : (string * ufunction list * uconstant list) list)
|
||||
|
||||
(* Boxed integers *)
|
||||
|
||||
|
@ -1320,10 +1320,13 @@ let rec is_unboxed_number env e =
|
|||
| Some (_, bn) -> Boxed bn
|
||||
end
|
||||
|
||||
| Uconst(Uconst_ref(_, Uconst_float _)) -> Boxed Boxed_float
|
||||
| Uconst(Uconst_ref(_, Uconst_int32 _)) -> Boxed (Boxed_integer Pint32)
|
||||
| Uconst(Uconst_ref(_, Uconst_int64 _)) -> Boxed (Boxed_integer Pint64)
|
||||
| Uconst(Uconst_ref(_, Uconst_nativeint _)) ->
|
||||
| Uconst(Uconst_ref(_, Some (Uconst_float _))) ->
|
||||
Boxed Boxed_float
|
||||
| Uconst(Uconst_ref(_, Some (Uconst_int32 _))) ->
|
||||
Boxed (Boxed_integer Pint32)
|
||||
| Uconst(Uconst_ref(_, Some (Uconst_int64 _))) ->
|
||||
Boxed (Boxed_integer Pint64)
|
||||
| Uconst(Uconst_ref(_, Some (Uconst_nativeint _))) ->
|
||||
Boxed (Boxed_integer Pnativeint)
|
||||
| Uprim(p, _, _) ->
|
||||
begin match simplif_primitive p with
|
||||
|
@ -1406,7 +1409,7 @@ let rec transl env e =
|
|||
transl_constant sc
|
||||
| Uclosure(fundecls, []) ->
|
||||
let lbl = Compilenv.new_const_symbol() in
|
||||
constant_closures := (lbl, fundecls) :: !constant_closures;
|
||||
constant_closures := (lbl, fundecls, []) :: !constant_closures;
|
||||
List.iter (fun f -> Queue.add f functions) fundecls;
|
||||
Cconst_symbol lbl
|
||||
| Uclosure(fundecls, clos_vars) ->
|
||||
|
@ -1644,6 +1647,8 @@ let rec transl env e =
|
|||
| Some (unboxed_id, bn) ->
|
||||
return_unit(Cassign(unboxed_id, transl_unbox_number env bn exp))
|
||||
end
|
||||
| Uunreachable ->
|
||||
Cop(Cload Word_int, [Cconst_int 0])
|
||||
|
||||
and transl_ccall env prim args dbg =
|
||||
let transl_arg native_repr arg =
|
||||
|
@ -2195,15 +2200,15 @@ and transl_prim_3 env p arg1 arg2 arg3 dbg =
|
|||
fatal_error "Cmmgen.transl_prim_3"
|
||||
|
||||
and transl_unbox_float env = function
|
||||
Uconst(Uconst_ref(_, Uconst_float f)) -> Cconst_float f
|
||||
Uconst(Uconst_ref(_, Some (Uconst_float f))) -> Cconst_float f
|
||||
| exp -> unbox_float(transl env exp)
|
||||
|
||||
and transl_unbox_int env bi = function
|
||||
Uconst(Uconst_ref(_, Uconst_int32 n)) ->
|
||||
Uconst(Uconst_ref(_, Some (Uconst_int32 n))) ->
|
||||
Cconst_natint (Nativeint.of_int32 n)
|
||||
| Uconst(Uconst_ref(_, Uconst_nativeint n)) ->
|
||||
| Uconst(Uconst_ref(_, Some (Uconst_nativeint n))) ->
|
||||
Cconst_natint n
|
||||
| Uconst(Uconst_ref(_, Uconst_int64 n)) ->
|
||||
| Uconst(Uconst_ref(_, Some (Uconst_int64 n))) ->
|
||||
if size_int = 8 then
|
||||
Cconst_natint (Int64.to_nativeint n)
|
||||
else
|
||||
|
@ -2405,7 +2410,7 @@ let rec transl_all_functions already_translated cont =
|
|||
(transl_function f :: cont)
|
||||
end
|
||||
with Queue.Empty ->
|
||||
cont
|
||||
cont, already_translated
|
||||
|
||||
(* Emit structured constants *)
|
||||
|
||||
|
@ -2437,6 +2442,10 @@ let rec emit_structured_constant symb cst cont =
|
|||
| Uconst_float_array fields ->
|
||||
emit_block (floatarray_header (List.length fields)) symb
|
||||
(Misc.map_end (fun f -> Cdouble f) fields cont)
|
||||
| Uconst_closure(fundecls, lbl, fv) ->
|
||||
constant_closures := (lbl, fundecls, fv) :: !constant_closures;
|
||||
List.iter (fun f -> Queue.add f functions) fundecls;
|
||||
cont
|
||||
|
||||
and emit_constant cst cont =
|
||||
match cst with
|
||||
|
@ -2474,12 +2483,13 @@ and emit_boxed_int64_constant n cont =
|
|||
|
||||
(* Emit constant closures *)
|
||||
|
||||
let emit_constant_closure symb fundecls cont =
|
||||
let emit_constant_closure symb fundecls clos_vars cont =
|
||||
match fundecls with
|
||||
[] -> assert false
|
||||
| f1 :: remainder ->
|
||||
let rec emit_others pos = function
|
||||
[] -> cont
|
||||
[] ->
|
||||
List.fold_right emit_constant clos_vars cont
|
||||
| f2 :: rem ->
|
||||
if f2.arity = 1 then
|
||||
Cint(infix_header pos) ::
|
||||
|
@ -2492,7 +2502,8 @@ let emit_constant_closure symb fundecls cont =
|
|||
Cint(Nativeint.of_int (f2.arity lsl 1 + 1)) ::
|
||||
Csymbol_address f2.label ::
|
||||
emit_others (pos + 4) rem in
|
||||
Cint(black_closure_header (fundecls_size fundecls)) ::
|
||||
Cint(black_closure_header (fundecls_size fundecls
|
||||
+ List.length clos_vars)) ::
|
||||
Cdefine_symbol symb ::
|
||||
if f1.arity = 1 then
|
||||
Csymbol_address f1.label ::
|
||||
|
@ -2517,12 +2528,25 @@ let emit_all_constants cont =
|
|||
c:= Cdata(cst):: !c)
|
||||
(Compilenv.structured_constants());
|
||||
List.iter
|
||||
(fun (symb, fundecls) ->
|
||||
c := Cdata(emit_constant_closure symb fundecls []) :: !c)
|
||||
(fun (symb, fundecls, clos_vars) ->
|
||||
c := Cdata(emit_constant_closure symb fundecls clos_vars []) :: !c)
|
||||
!constant_closures;
|
||||
constant_closures := [];
|
||||
Compilenv.clear_structured_constants ();
|
||||
!c
|
||||
|
||||
let transl_all_functions_and_emit_all_constants cont =
|
||||
let rec aux already_translated cont =
|
||||
if Compilenv.structured_constants () = [] &&
|
||||
Queue.is_empty functions
|
||||
then cont
|
||||
else
|
||||
let cont, set = transl_all_functions already_translated cont in
|
||||
let cont = emit_all_constants cont in
|
||||
aux already_translated cont
|
||||
in
|
||||
aux StringSet.empty cont
|
||||
|
||||
(* Build the table of GC roots for toplevel modules *)
|
||||
|
||||
let emit_module_roots_table ~symbols cont =
|
||||
|
@ -2542,9 +2566,8 @@ let compunit size ulam =
|
|||
fun_args = [];
|
||||
fun_body = init_code; fun_fast = false;
|
||||
fun_dbg = Debuginfo.none }] in
|
||||
let c2 = transl_all_functions StringSet.empty c1 in
|
||||
let c3 = emit_all_constants c2 in
|
||||
let c4 = emit_module_roots_table ~symbols:[glob] c3 in
|
||||
let c2 = transl_all_functions_and_emit_all_constants c1 in
|
||||
let c3 = emit_module_roots_table ~symbols:[glob] c2 in
|
||||
let space =
|
||||
(* These words will be registered as roots and as such must contain
|
||||
valid values, in case we are in no-naked-pointers mode. Likewise
|
||||
|
@ -2556,7 +2579,7 @@ let compunit size ulam =
|
|||
in
|
||||
Cdata ([Cint(black_block_header 0 size);
|
||||
Cglobal_symbol glob;
|
||||
Cdefine_symbol glob] @ space) :: c4
|
||||
Cdefine_symbol glob] @ space) :: c3
|
||||
|
||||
(*
|
||||
CAMLprim value caml_cache_public_method (value meths, value tag, value *cache)
|
||||
|
@ -2945,7 +2968,7 @@ let predef_exception i name =
|
|||
emit_structured_constant symname
|
||||
(Uconst_block(Obj.object_tag,
|
||||
[
|
||||
Uconst_ref(label, cst);
|
||||
Uconst_ref(label, Some cst);
|
||||
Uconst_int (-i-1);
|
||||
])) cont)
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ exception Error of error
|
|||
let global_infos_table =
|
||||
(Hashtbl.create 17 : (string, unit_infos option) Hashtbl.t)
|
||||
|
||||
let sourcefile = ref None
|
||||
|
||||
module CstMap =
|
||||
Map.Make(struct
|
||||
type t = Clambda.ustructured_constant
|
||||
|
@ -79,9 +81,10 @@ let symbolname_for_pack pack name =
|
|||
Buffer.contents b
|
||||
|
||||
|
||||
let reset ?packname name =
|
||||
let reset ?packname ~source_provenance:file name =
|
||||
Hashtbl.clear global_infos_table;
|
||||
let symbol = symbolname_for_pack packname name in
|
||||
sourcefile := Some file;
|
||||
current_unit.ui_name <- name;
|
||||
current_unit.ui_symbol <- symbol;
|
||||
current_unit.ui_defines <- [symbol];
|
||||
|
@ -100,6 +103,11 @@ let current_unit_infos () =
|
|||
let current_unit_name () =
|
||||
current_unit.ui_name
|
||||
|
||||
let current_build () =
|
||||
match !sourcefile with
|
||||
| None -> assert false
|
||||
| Some v -> v
|
||||
|
||||
let make_symbol ?(unitname = current_unit.ui_symbol) idopt =
|
||||
let prefix = "caml" ^ unitname in
|
||||
match idopt with
|
||||
|
@ -152,15 +160,21 @@ let get_global_info global_ident = (
|
|||
Hashtbl.find global_infos_table modname
|
||||
with Not_found ->
|
||||
let (infos, crc) =
|
||||
try
|
||||
let filename =
|
||||
find_in_path_uncap !load_path (modname ^ ".cmx") in
|
||||
let (ui, crc) = read_unit_info filename in
|
||||
if ui.ui_name <> modname then
|
||||
raise(Error(Illegal_renaming(modname, ui.ui_name, filename)));
|
||||
(Some ui, Some crc)
|
||||
with Not_found ->
|
||||
(None, None) in
|
||||
if Env.is_imported_opaque modname then (None, None)
|
||||
else begin
|
||||
try
|
||||
let filename =
|
||||
find_in_path_uncap !load_path (modname ^ ".cmx") in
|
||||
let (ui, crc) = read_unit_info filename in
|
||||
if ui.ui_name <> modname then
|
||||
raise(Error(Illegal_renaming(modname, ui.ui_name, filename)));
|
||||
(Some ui, Some crc)
|
||||
with Not_found ->
|
||||
let warn = Warnings.No_cmx_file modname in
|
||||
Location.prerr_warning Location.none warn;
|
||||
(None, None)
|
||||
end
|
||||
in
|
||||
current_unit.ui_imports_cmx <-
|
||||
(modname, crc) :: current_unit.ui_imports_cmx;
|
||||
Hashtbl.add global_infos_table modname infos;
|
||||
|
@ -192,7 +206,11 @@ let symbol_for_global id =
|
|||
if Ident.is_predef_exn id then
|
||||
"caml_exn_" ^ Ident.name id
|
||||
else begin
|
||||
match get_global_info id with
|
||||
let unitname = Ident.name id in
|
||||
match
|
||||
try ignore (Hashtbl.find toplevel_approx unitname); None
|
||||
with Not_found -> get_global_info id
|
||||
with
|
||||
| None -> make_symbol ~unitname:(Ident.name id) None
|
||||
| Some ui -> make_symbol ~unitname:ui.ui_symbol None
|
||||
end
|
||||
|
@ -271,6 +289,9 @@ let new_structured_constant cst ~shared =
|
|||
let add_exported_constant s =
|
||||
Hashtbl.replace exported_constants s ()
|
||||
|
||||
let clear_structured_constants () =
|
||||
structured_constants := structured_constants_empty
|
||||
|
||||
let structured_constants () =
|
||||
List.map
|
||||
(fun (lbl, cst) ->
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
|
||||
open Cmx_format
|
||||
|
||||
val reset: ?packname:string -> string -> unit
|
||||
val reset: ?packname:string -> source_provenance:Timings.source_provenance ->
|
||||
string -> unit
|
||||
(* Reset the environment and record the name of the unit being
|
||||
compiled (arg). Optional argument is [-for-pack] prefix. *)
|
||||
|
||||
|
@ -24,6 +25,10 @@ val current_unit_infos: unit -> unit_infos
|
|||
val current_unit_name: unit -> string
|
||||
(* Return the name of the unit being compiled *)
|
||||
|
||||
val current_build: unit -> Timings.source_provenance
|
||||
(* Return the kind of build source being compiled. If it is a
|
||||
file compilation it also provides the filename. *)
|
||||
|
||||
val make_symbol: ?unitname:string -> string option -> string
|
||||
(* [make_symbol ~unitname:u None] returns the asm symbol that
|
||||
corresponds to the compilation unit [u] (default: the current unit).
|
||||
|
@ -61,6 +66,7 @@ val new_structured_constant:
|
|||
string
|
||||
val structured_constants:
|
||||
unit -> (string * bool * Clambda.ustructured_constant) list
|
||||
val clear_structured_constants: unit -> unit
|
||||
val add_exported_constant: string -> unit
|
||||
|
||||
type structured_constants
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
type value_string_contents =
|
||||
| Contents of string
|
||||
| Unknown_or_mutable
|
||||
|
||||
type value_string = {
|
||||
contents : value_string_contents;
|
||||
size : int;
|
||||
}
|
||||
|
||||
type value_float_array_contents =
|
||||
| Contents of float option array
|
||||
| Unknown_or_mutable
|
||||
|
||||
type value_float_array = {
|
||||
contents : value_float_array_contents;
|
||||
size : int;
|
||||
}
|
||||
|
||||
type descr =
|
||||
| Value_block of Tag.t * approx array
|
||||
| Value_mutable_block of Tag.t * int
|
||||
| Value_int of int
|
||||
| Value_char of char
|
||||
| Value_constptr of int
|
||||
| Value_float of float
|
||||
| Value_float_array of value_float_array
|
||||
| Value_boxed_int : 'a Simple_value_approx.boxed_int * 'a -> descr
|
||||
| Value_string of value_string
|
||||
| Value_closure of value_closure
|
||||
| Value_set_of_closures of value_set_of_closures
|
||||
|
||||
and value_closure = {
|
||||
closure_id : Closure_id.t;
|
||||
set_of_closures : value_set_of_closures;
|
||||
}
|
||||
|
||||
and value_set_of_closures = {
|
||||
set_of_closures_id : Set_of_closures_id.t;
|
||||
bound_vars : approx Var_within_closure.Map.t;
|
||||
results : approx Closure_id.Map.t;
|
||||
aliased_symbol : Symbol.t option;
|
||||
}
|
||||
|
||||
and approx =
|
||||
| Value_unknown
|
||||
| Value_id of Export_id.t
|
||||
| Value_symbol of Symbol.t
|
||||
|
||||
let equal_approx (a1:approx) (a2:approx) =
|
||||
match a1, a2 with
|
||||
| Value_unknown, Value_unknown ->
|
||||
true
|
||||
| Value_id id1, Value_id id2 ->
|
||||
Export_id.equal id1 id2
|
||||
| Value_symbol s1, Value_symbol s2 ->
|
||||
Symbol.equal s1 s2
|
||||
| (Value_unknown | Value_symbol _ | Value_id _),
|
||||
(Value_unknown | Value_symbol _ | Value_id _) ->
|
||||
false
|
||||
|
||||
let equal_array eq a1 a2 =
|
||||
Array.length a1 = Array.length a2 &&
|
||||
try
|
||||
Array.iteri (fun i v1 -> if not (eq a2.(i) v1) then raise Exit) a1;
|
||||
true
|
||||
with Exit -> false
|
||||
|
||||
let equal_option eq o1 o2 =
|
||||
match o1, o2 with
|
||||
| None, None -> true
|
||||
| Some v1, Some v2 -> eq v1 v2
|
||||
| Some _, None | None, Some _ -> false
|
||||
|
||||
let equal_set_of_closures (s1:value_set_of_closures) (s2:value_set_of_closures) =
|
||||
Set_of_closures_id.equal s1.set_of_closures_id s2.set_of_closures_id &&
|
||||
Var_within_closure.Map.equal equal_approx s1.bound_vars s2.bound_vars &&
|
||||
Closure_id.Map.equal equal_approx s1.results s2.results &&
|
||||
equal_option Symbol.equal s1.aliased_symbol s2.aliased_symbol
|
||||
|
||||
let equal_descr (d1:descr) (d2:descr) : bool =
|
||||
match d1, d2 with
|
||||
| Value_block (t1, f1), Value_block (t2, f2) ->
|
||||
Tag.equal t1 t2 && equal_array equal_approx f1 f2
|
||||
| Value_mutable_block (t1, s1), Value_mutable_block (t2, s2) ->
|
||||
Tag.equal t1 t2 &&
|
||||
s1 = s2
|
||||
| Value_int i1, Value_int i2 ->
|
||||
i1 = i2
|
||||
| Value_char c1, Value_char c2 ->
|
||||
c1 = c2
|
||||
| Value_constptr i1, Value_constptr i2 ->
|
||||
i1 = i2
|
||||
| Value_float f1, Value_float f2 ->
|
||||
f1 = f2
|
||||
| Value_float_array s1, Value_float_array s2 ->
|
||||
s1 = s2
|
||||
| Value_boxed_int (t1, v1), Value_boxed_int (t2, v2) ->
|
||||
Simple_value_approx.equal_boxed_int t1 v1 t2 v2
|
||||
| Value_string s1, Value_string s2 ->
|
||||
s1 = s2
|
||||
| Value_closure c1, Value_closure c2 ->
|
||||
Closure_id.equal c1.closure_id c2.closure_id &&
|
||||
equal_set_of_closures c1.set_of_closures c2.set_of_closures
|
||||
| Value_set_of_closures s1, Value_set_of_closures s2 ->
|
||||
equal_set_of_closures s1 s2
|
||||
| ( Value_block (_, _) | Value_mutable_block (_, _) | Value_int _
|
||||
| Value_char _ | Value_constptr _ | Value_float _ | Value_float_array _
|
||||
| Value_boxed_int _ | Value_string _ | Value_closure _
|
||||
| Value_set_of_closures _ ),
|
||||
( Value_block (_, _) | Value_mutable_block (_, _) | Value_int _
|
||||
| Value_char _ | Value_constptr _ | Value_float _ | Value_float_array _
|
||||
| Value_boxed_int _ | Value_string _ | Value_closure _
|
||||
| Value_set_of_closures _ ) ->
|
||||
false
|
||||
|
||||
type t = {
|
||||
sets_of_closures : Flambda.function_declarations Set_of_closures_id.Map.t;
|
||||
closures : Flambda.function_declarations Closure_id.Map.t;
|
||||
values : descr Export_id.Map.t Compilation_unit.Map.t;
|
||||
globals : approx Ident.Map.t;
|
||||
symbol_id : Export_id.t Symbol.Map.t;
|
||||
offset_fun : int Closure_id.Map.t;
|
||||
offset_fv : int Var_within_closure.Map.t;
|
||||
constant_sets_of_closures : Set_of_closures_id.Set.t;
|
||||
invariant_params : Variable.Set.t Variable.Map.t Set_of_closures_id.Map.t;
|
||||
}
|
||||
|
||||
let empty : t = {
|
||||
sets_of_closures = Set_of_closures_id.Map.empty;
|
||||
closures = Closure_id.Map.empty;
|
||||
values = Compilation_unit.Map.empty;
|
||||
globals = Ident.Map.empty;
|
||||
symbol_id = Symbol.Map.empty;
|
||||
offset_fun = Closure_id.Map.empty;
|
||||
offset_fv = Var_within_closure.Map.empty;
|
||||
constant_sets_of_closures = Set_of_closures_id.Set.empty;
|
||||
invariant_params = Set_of_closures_id.Map.empty;
|
||||
}
|
||||
|
||||
let create ~sets_of_closures ~closures ~values ~globals ~symbol_id
|
||||
~offset_fun ~offset_fv ~constant_sets_of_closures
|
||||
~invariant_params =
|
||||
{ sets_of_closures;
|
||||
closures;
|
||||
values;
|
||||
globals;
|
||||
symbol_id;
|
||||
offset_fun;
|
||||
offset_fv;
|
||||
constant_sets_of_closures;
|
||||
invariant_params;
|
||||
}
|
||||
|
||||
let add_clambda_info t ~offset_fun ~offset_fv ~constant_sets_of_closures =
|
||||
assert (Closure_id.Map.cardinal t.offset_fun = 0);
|
||||
assert (Var_within_closure.Map.cardinal t.offset_fv = 0);
|
||||
assert (Set_of_closures_id.Set.cardinal t.constant_sets_of_closures = 0);
|
||||
{ t with offset_fun; offset_fv; constant_sets_of_closures; }
|
||||
|
||||
let merge (t1 : t) (t2 : t) : t =
|
||||
let eidmap_disjoint_union ?eq map1 map2 =
|
||||
Compilation_unit.Map.merge (fun _id map1 map2 ->
|
||||
match map1, map2 with
|
||||
| None, None -> None
|
||||
| None, Some map
|
||||
| Some map, None -> Some map
|
||||
| Some map1, Some map2 ->
|
||||
Some (Export_id.Map.disjoint_union ?eq map1 map2))
|
||||
map1 map2
|
||||
in
|
||||
let int_eq (i : int) j = i = j in
|
||||
{ values = eidmap_disjoint_union ~eq:equal_descr t1.values t2.values;
|
||||
globals = Ident.Map.disjoint_union t1.globals t2.globals;
|
||||
sets_of_closures =
|
||||
Set_of_closures_id.Map.disjoint_union t1.sets_of_closures
|
||||
t2.sets_of_closures;
|
||||
closures = Closure_id.Map.disjoint_union t1.closures t2.closures;
|
||||
symbol_id = Symbol.Map.disjoint_union t1.symbol_id t2.symbol_id;
|
||||
offset_fun = Closure_id.Map.disjoint_union
|
||||
~eq:int_eq t1.offset_fun t2.offset_fun;
|
||||
offset_fv = Var_within_closure.Map.disjoint_union
|
||||
~eq:int_eq t1.offset_fv t2.offset_fv;
|
||||
constant_sets_of_closures =
|
||||
Set_of_closures_id.Set.union t1.constant_sets_of_closures
|
||||
t2.constant_sets_of_closures;
|
||||
invariant_params =
|
||||
Set_of_closures_id.Map.disjoint_union
|
||||
~eq:(Variable.Map.equal Variable.Set.equal)
|
||||
t1.invariant_params t2.invariant_params;
|
||||
}
|
||||
|
||||
let find_value eid map =
|
||||
let unit_map = Compilation_unit.Map.find (Export_id.get_compilation_unit eid) map in
|
||||
Export_id.Map.find eid unit_map
|
||||
|
||||
let find_description (t : t) eid =
|
||||
find_value eid t.values
|
||||
|
||||
let nest_eid_map map =
|
||||
let add_map eid v map =
|
||||
let unit = Export_id.get_compilation_unit eid in
|
||||
let m =
|
||||
try Compilation_unit.Map.find unit map
|
||||
with Not_found -> Export_id.Map.empty
|
||||
in
|
||||
Compilation_unit.Map.add unit (Export_id.Map.add eid v m) map
|
||||
in
|
||||
Export_id.Map.fold add_map map Compilation_unit.Map.empty
|
||||
|
||||
let print_approx ppf (t : t) =
|
||||
let values = t.values in
|
||||
let fprintf = Format.fprintf in
|
||||
let printed = ref Export_id.Set.empty in
|
||||
let recorded_symbol = ref Symbol.Set.empty in
|
||||
let symbols_to_print = Queue.create () in
|
||||
let printed_set_of_closures = ref Set_of_closures_id.Set.empty in
|
||||
let rec print_approx ppf (approx : approx) =
|
||||
match approx with
|
||||
| Value_unknown -> fprintf ppf "?"
|
||||
| Value_id id ->
|
||||
if Export_id.Set.mem id !printed then
|
||||
fprintf ppf "(%a: _)" Export_id.print id
|
||||
else begin
|
||||
try
|
||||
let descr = find_value id values in
|
||||
printed := Export_id.Set.add id !printed;
|
||||
fprintf ppf "@[<hov 2>(%a:@ %a)@]" Export_id.print id print_descr descr
|
||||
with Not_found ->
|
||||
fprintf ppf "(%a: Not available)" Export_id.print id
|
||||
end
|
||||
| Value_symbol sym ->
|
||||
if not (Symbol.Set.mem sym !recorded_symbol) then begin
|
||||
recorded_symbol := Symbol.Set.add sym !recorded_symbol;
|
||||
Queue.push sym symbols_to_print;
|
||||
end;
|
||||
Symbol.print ppf sym
|
||||
and print_descr ppf (descr : descr) =
|
||||
match descr with
|
||||
| Value_int i -> Format.pp_print_int ppf i
|
||||
| Value_char c -> fprintf ppf "%c" c
|
||||
| Value_constptr i -> fprintf ppf "%ip" i
|
||||
| Value_block (tag, fields) ->
|
||||
fprintf ppf "[%a:%a]" Tag.print tag print_fields fields
|
||||
| Value_mutable_block (tag, size) ->
|
||||
fprintf ppf "[mutable %a:%i]" Tag.print tag size
|
||||
| Value_closure {closure_id; set_of_closures} ->
|
||||
fprintf ppf "(closure %a, %a)" Closure_id.print closure_id
|
||||
print_set_of_closures set_of_closures
|
||||
| Value_set_of_closures set_of_closures ->
|
||||
fprintf ppf "(set_of_closures %a)" print_set_of_closures set_of_closures
|
||||
| Value_string { contents; size } ->
|
||||
begin match contents with
|
||||
| Unknown_or_mutable -> Format.fprintf ppf "string %i" size
|
||||
| Contents s ->
|
||||
let s =
|
||||
if size > 10
|
||||
then String.sub s 0 8 ^ "..."
|
||||
else s
|
||||
in
|
||||
Format.fprintf ppf "string %i %S" size s
|
||||
end
|
||||
| Value_float f -> Format.pp_print_float ppf f
|
||||
| Value_float_array float_array ->
|
||||
Format.fprintf ppf "float_array%s %i"
|
||||
(match float_array.contents with
|
||||
| Unknown_or_mutable -> ""
|
||||
| Contents _ -> "_imm")
|
||||
float_array.size
|
||||
| Value_boxed_int (t, i) ->
|
||||
let module A = Simple_value_approx in
|
||||
match t with
|
||||
| A.Int32 -> Format.fprintf ppf "%li" i
|
||||
| A.Int64 -> Format.fprintf ppf "%Li" i
|
||||
| A.Nativeint -> Format.fprintf ppf "%ni" i
|
||||
and print_fields ppf fields =
|
||||
Array.iter (fun approx -> fprintf ppf "%a@ " print_approx approx) fields
|
||||
and print_set_of_closures ppf
|
||||
{ set_of_closures_id; bound_vars; aliased_symbol } =
|
||||
if Set_of_closures_id.Set.mem set_of_closures_id !printed_set_of_closures
|
||||
then fprintf ppf "%a" Set_of_closures_id.print set_of_closures_id
|
||||
else begin
|
||||
printed_set_of_closures :=
|
||||
Set_of_closures_id.Set.add set_of_closures_id !printed_set_of_closures;
|
||||
let print_alias ppf = function
|
||||
| None -> ()
|
||||
| Some symbol ->
|
||||
Format.fprintf ppf "@ (alias: %a)" Symbol.print symbol
|
||||
in
|
||||
fprintf ppf "{%a: %a%a}"
|
||||
Set_of_closures_id.print set_of_closures_id
|
||||
print_binding bound_vars
|
||||
print_alias aliased_symbol
|
||||
end
|
||||
and print_binding ppf bound_vars =
|
||||
Var_within_closure.Map.iter (fun clos_id approx ->
|
||||
fprintf ppf "%a -> %a,@ "
|
||||
Var_within_closure.print clos_id
|
||||
print_approx approx)
|
||||
bound_vars
|
||||
in
|
||||
let print_approxs id approx =
|
||||
fprintf ppf "%a -> %a;@ " Ident.print id print_approx approx
|
||||
in
|
||||
let rec print_recorded_symbols () =
|
||||
if not (Queue.is_empty symbols_to_print) then begin
|
||||
let sym = Queue.pop symbols_to_print in
|
||||
begin match Symbol.Map.find sym t.symbol_id with
|
||||
| exception Not_found -> ()
|
||||
| id ->
|
||||
fprintf ppf "@[<hov 2>%a:@ %a@];@ "
|
||||
Symbol.print sym
|
||||
print_approx (Value_id id)
|
||||
end;
|
||||
print_recorded_symbols ();
|
||||
end
|
||||
in
|
||||
fprintf ppf "@[<hov 2>Globals:@ ";
|
||||
Ident.Map.iter print_approxs t.globals;
|
||||
fprintf ppf "@]@ @[<hov 2>Symbols:@ ";
|
||||
print_recorded_symbols ();
|
||||
fprintf ppf "@]"
|
||||
|
||||
let print_offsets ppf (t : t) =
|
||||
Format.fprintf ppf "@[<v 2>offset_fun:@ ";
|
||||
Closure_id.Map.iter (fun cid off ->
|
||||
Format.fprintf ppf "%a -> %i@ "
|
||||
Closure_id.print cid off) t.offset_fun;
|
||||
Format.fprintf ppf "@]@ @[<v 2>offset_fv:@ ";
|
||||
Var_within_closure.Map.iter (fun vid off ->
|
||||
Format.fprintf ppf "%a -> %i@ "
|
||||
Var_within_closure.print vid off) t.offset_fv;
|
||||
Format.fprintf ppf "@]@ "
|
||||
|
||||
let print_all ppf (t : t) =
|
||||
let fprintf = Format.fprintf in
|
||||
fprintf ppf "approxs@ %a@.@."
|
||||
print_approx t;
|
||||
fprintf ppf "functions@ %a@.@."
|
||||
(Set_of_closures_id.Map.print Flambda.print_function_declarations)
|
||||
t.sets_of_closures
|
|
@ -0,0 +1,150 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(** Exported information (that is to say, information written into a .cmx
|
||||
file) about a compilation unit. *)
|
||||
|
||||
type value_string_contents =
|
||||
| Contents of string
|
||||
| Unknown_or_mutable
|
||||
|
||||
type value_string = {
|
||||
contents : value_string_contents;
|
||||
size : int;
|
||||
}
|
||||
|
||||
type value_float_array_contents =
|
||||
| Contents of float option array
|
||||
| Unknown_or_mutable
|
||||
|
||||
type value_float_array = {
|
||||
contents : value_float_array_contents;
|
||||
size : int;
|
||||
}
|
||||
|
||||
type descr =
|
||||
| Value_block of Tag.t * approx array
|
||||
| Value_mutable_block of Tag.t * int
|
||||
| Value_int of int
|
||||
| Value_char of char
|
||||
| Value_constptr of int
|
||||
| Value_float of float
|
||||
| Value_float_array of value_float_array
|
||||
| Value_boxed_int : 'a Simple_value_approx.boxed_int * 'a -> descr
|
||||
| Value_string of value_string
|
||||
| Value_closure of value_closure
|
||||
| Value_set_of_closures of value_set_of_closures
|
||||
|
||||
and value_closure = {
|
||||
closure_id : Closure_id.t;
|
||||
set_of_closures : value_set_of_closures;
|
||||
}
|
||||
|
||||
and value_set_of_closures = {
|
||||
set_of_closures_id : Set_of_closures_id.t;
|
||||
bound_vars : approx Var_within_closure.Map.t;
|
||||
results : approx Closure_id.Map.t;
|
||||
aliased_symbol : Symbol.t option;
|
||||
}
|
||||
|
||||
(* CR-soon mshinwell: Fix the export information so we can correctly
|
||||
propagate "unresolved due to..." in the manner of [Simple_value_approx].
|
||||
Unfortunately this seems to be complicated by the fact that, during
|
||||
[Import_approx], resolution can fail not only due to missing symbols but
|
||||
also due to missing export IDs. The argument type of
|
||||
[Simple_value_approx.t] may need updating to reflect this (make the
|
||||
symbol optional? It's only for debugging anyway.) *)
|
||||
and approx =
|
||||
| Value_unknown
|
||||
| Value_id of Export_id.t
|
||||
| Value_symbol of Symbol.t
|
||||
|
||||
(** A structure that describes what a single compilation unit exports. *)
|
||||
type t = private {
|
||||
sets_of_closures : Flambda.function_declarations Set_of_closures_id.Map.t;
|
||||
(** Code of exported functions indexed by set of closures IDs. *)
|
||||
closures : Flambda.function_declarations Closure_id.Map.t;
|
||||
(** Code of exported functions indexed by closure IDs. *)
|
||||
values : descr Export_id.Map.t Compilation_unit.Map.t;
|
||||
(** Structure of exported values. *)
|
||||
globals : approx Ident.Map.t;
|
||||
(** Global variables provided by the unit: usually only the top-level
|
||||
module identifier, but packs may contain more than one. *)
|
||||
symbol_id : Export_id.t Symbol.Map.t;
|
||||
(** Associates symbols and values. *)
|
||||
offset_fun : int Closure_id.Map.t;
|
||||
(** Positions of function pointers in their closures. *)
|
||||
offset_fv : int Var_within_closure.Map.t;
|
||||
(** Positions of value pointers in their closures. *)
|
||||
constant_sets_of_closures : Set_of_closures_id.Set.t;
|
||||
(* CR mshinwell for pchambart: Add comment *)
|
||||
invariant_params : Variable.Set.t Variable.Map.t Set_of_closures_id.Map.t;
|
||||
(* Function parameters known to be invariant (see [Invariant_params])
|
||||
indexed by set of closures ID. *)
|
||||
}
|
||||
|
||||
(** Export information for a compilation unit that exports nothing. *)
|
||||
val empty : t
|
||||
|
||||
(** Create a new export information structure. *)
|
||||
val create
|
||||
: sets_of_closures:Flambda.function_declarations Set_of_closures_id.Map.t
|
||||
-> closures:Flambda.function_declarations Closure_id.Map.t
|
||||
-> values:descr Export_id.Map.t Compilation_unit.Map.t
|
||||
-> globals:approx Ident.Map.t
|
||||
-> symbol_id:Export_id.t Symbol.Map.t
|
||||
-> offset_fun:int Closure_id.Map.t
|
||||
-> offset_fv:int Var_within_closure.Map.t
|
||||
-> constant_sets_of_closures:Set_of_closures_id.Set.t
|
||||
-> invariant_params:Variable.Set.t Variable.Map.t Set_of_closures_id.Map.t
|
||||
-> t
|
||||
|
||||
(* CR-someday pchambart: Should we separate [t] in 2 types: one created by the
|
||||
current [create] function, returned by [Build_export_info]. And
|
||||
another built using t and offset_informations returned by
|
||||
[flambda_to_clambda] ?
|
||||
mshinwell: I think we should, but after we've done the first release.
|
||||
*)
|
||||
(** Record information about the layout of closures and which sets of
|
||||
closures are constant. These are all worked out during the
|
||||
[Flambda_to_clambda] pass. *)
|
||||
val add_clambda_info
|
||||
: t
|
||||
-> offset_fun:int Closure_id.Map.t
|
||||
-> offset_fv:int Var_within_closure.Map.t
|
||||
-> constant_sets_of_closures:Set_of_closures_id.Set.t
|
||||
-> t
|
||||
|
||||
(** Union of export information. Verifies that there are no identifier
|
||||
clashes. *)
|
||||
val merge : t -> t -> t
|
||||
|
||||
(** Look up the description of an exported value given its export ID. *)
|
||||
val find_description
|
||||
: t
|
||||
-> Export_id.t
|
||||
-> descr
|
||||
|
||||
(** Partition a mapping from export IDs by compilation unit. *)
|
||||
val nest_eid_map
|
||||
: 'a Export_id.Map.t
|
||||
-> 'a Export_id.Map.t Compilation_unit.Map.t
|
||||
|
||||
(**/**)
|
||||
(* Debug printing functions. *)
|
||||
val print_approx : Format.formatter -> t -> unit
|
||||
val print_offsets : Format.formatter -> t -> unit
|
||||
val print_all : Format.formatter -> t -> unit
|
|
@ -0,0 +1,143 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
let rename_id_state = Export_id.Tbl.create 100
|
||||
|
||||
(* Rename export identifiers' compilation units to denote that they now
|
||||
live within a pack. *)
|
||||
let import_eid_for_pack units pack id =
|
||||
try Export_id.Tbl.find rename_id_state id
|
||||
with Not_found ->
|
||||
let unit_id = Export_id.get_compilation_unit id in
|
||||
let id' =
|
||||
if Compilation_unit.Set.mem unit_id units
|
||||
then Export_id.create ?name:(Export_id.name id) pack
|
||||
else id
|
||||
in
|
||||
Export_id.Tbl.add rename_id_state id id';
|
||||
id'
|
||||
|
||||
(* Similar to [import_eid_for_pack], but for symbols. *)
|
||||
let import_symbol_for_pack units pack symbol =
|
||||
let compilation_unit = Symbol.compilation_unit symbol in
|
||||
if Compilation_unit.Set.mem compilation_unit units
|
||||
then Symbol.import_for_pack ~pack symbol
|
||||
else symbol
|
||||
|
||||
let import_approx_for_pack units pack (approx : Export_info.approx)
|
||||
: Export_info.approx =
|
||||
match approx with
|
||||
| Value_symbol sym -> Value_symbol (import_symbol_for_pack units pack sym)
|
||||
| Value_id eid -> Value_id (import_eid_for_pack units pack eid)
|
||||
| Value_unknown -> Value_unknown
|
||||
|
||||
let import_set_of_closures units pack
|
||||
(set_of_closures : Export_info.value_set_of_closures)
|
||||
: Export_info.value_set_of_closures =
|
||||
{ set_of_closures_id = set_of_closures.set_of_closures_id;
|
||||
bound_vars =
|
||||
Var_within_closure.Map.map (import_approx_for_pack units pack)
|
||||
set_of_closures.bound_vars;
|
||||
results =
|
||||
Closure_id.Map.map (import_approx_for_pack units pack)
|
||||
set_of_closures.results;
|
||||
aliased_symbol =
|
||||
Misc.may_map
|
||||
(import_symbol_for_pack units pack)
|
||||
set_of_closures.aliased_symbol;
|
||||
}
|
||||
|
||||
let import_descr_for_pack units pack (descr : Export_info.descr)
|
||||
: Export_info.descr =
|
||||
match descr with
|
||||
| Value_int _
|
||||
| Value_char _
|
||||
| Value_constptr _
|
||||
| Value_string _
|
||||
| Value_float _
|
||||
| Value_float_array _
|
||||
| Export_info.Value_boxed_int _
|
||||
| Value_mutable_block _ as desc -> desc
|
||||
| Value_block (tag, fields) ->
|
||||
Value_block (tag, Array.map (import_approx_for_pack units pack) fields)
|
||||
| Value_closure { closure_id; set_of_closures } ->
|
||||
Value_closure {
|
||||
closure_id;
|
||||
set_of_closures = import_set_of_closures units pack set_of_closures;
|
||||
}
|
||||
| Value_set_of_closures set_of_closures ->
|
||||
Value_set_of_closures (import_set_of_closures units pack set_of_closures)
|
||||
|
||||
let import_code_for_pack units pack expr =
|
||||
Flambda_iterators.map_named (function
|
||||
| Symbol sym -> Symbol (import_symbol_for_pack units pack sym)
|
||||
| Read_symbol_field (sym, field) ->
|
||||
Read_symbol_field (import_symbol_for_pack units pack sym, field)
|
||||
| e -> e)
|
||||
expr
|
||||
|
||||
let import_function_declarations_for_pack units pack
|
||||
(function_decls : Flambda.function_declarations) =
|
||||
let funs =
|
||||
Variable.Map.map (fun (function_decl : Flambda.function_declaration) ->
|
||||
Flambda.create_function_declaration ~params:function_decl.params
|
||||
~body:(import_code_for_pack units pack function_decl.body)
|
||||
~stub:function_decl.stub ~dbg:function_decl.dbg
|
||||
~inline:function_decl.inline
|
||||
~is_a_functor:function_decl.is_a_functor)
|
||||
function_decls.funs
|
||||
in
|
||||
Flambda.update_function_declarations function_decls ~funs
|
||||
|
||||
let import_eidmap_for_pack units pack f map =
|
||||
Export_info.nest_eid_map
|
||||
(Compilation_unit.Map.fold
|
||||
(fun _ map acc -> Export_id.Map.disjoint_union map acc)
|
||||
(Compilation_unit.Map.map (fun map ->
|
||||
Export_id.Map.map_keys (import_eid_for_pack units pack)
|
||||
(Export_id.Map.map f map))
|
||||
map)
|
||||
Export_id.Map.empty)
|
||||
|
||||
let import_for_pack ~pack_units ~pack (exp : Export_info.t) =
|
||||
let import_sym = import_symbol_for_pack pack_units pack in
|
||||
let import_descr = import_descr_for_pack pack_units pack in
|
||||
let import_approx = import_approx_for_pack pack_units pack in
|
||||
let import_eid = import_eid_for_pack pack_units pack in
|
||||
let import_eidmap f map = import_eidmap_for_pack pack_units pack f map in
|
||||
let sets_of_closures =
|
||||
Set_of_closures_id.Map.map
|
||||
(import_function_declarations_for_pack pack_units pack)
|
||||
exp.sets_of_closures
|
||||
in
|
||||
(* The only reachable global identifier of a pack is the pack itself. *)
|
||||
let globals =
|
||||
Ident.Map.filter (fun unit _ ->
|
||||
Ident.same (Compilation_unit.get_persistent_ident pack) unit)
|
||||
exp.globals
|
||||
in
|
||||
Export_info.create ~sets_of_closures
|
||||
~closures:(Flambda_utils.make_closure_map' sets_of_closures)
|
||||
~globals:(Ident.Map.map import_approx globals)
|
||||
~offset_fun:exp.offset_fun
|
||||
~offset_fv:exp.offset_fv
|
||||
~values:(import_eidmap import_descr exp.values)
|
||||
~symbol_id:(Symbol.Map.map_keys import_sym
|
||||
(Symbol.Map.map import_eid exp.symbol_id))
|
||||
~constant_sets_of_closures:exp.constant_sets_of_closures
|
||||
~invariant_params:exp.invariant_params
|
||||
|
||||
let clear_import_state () = Export_id.Tbl.clear rename_id_state
|
|
@ -0,0 +1,32 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(** Transformations on export information that are only used for the
|
||||
building of packs. *)
|
||||
|
||||
(** Transform the information from [exported] to be
|
||||
suitable to be reexported as the information for a pack named [pack]
|
||||
containing units [pack_units].
|
||||
It mainly changes symbols of units [pack_units] to refer to
|
||||
[pack] instead. *)
|
||||
val import_for_pack
|
||||
: pack_units:Compilation_unit.Set.t
|
||||
-> pack:Compilation_unit.t
|
||||
-> Export_info.t
|
||||
-> Export_info.t
|
||||
|
||||
(** Drops the state after importing several units in the same pack. *)
|
||||
val clear_import_state : unit -> unit
|
|
@ -0,0 +1,684 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
type for_one_or_more_units = {
|
||||
fun_offset_table : int Closure_id.Map.t;
|
||||
fv_offset_table : int Var_within_closure.Map.t;
|
||||
closures : Flambda.function_declarations Closure_id.Map.t;
|
||||
constant_sets_of_closures : Set_of_closures_id.Set.t;
|
||||
}
|
||||
|
||||
type t = {
|
||||
current_unit : for_one_or_more_units;
|
||||
imported_units : for_one_or_more_units;
|
||||
}
|
||||
|
||||
type ('a, 'b) declaration_position =
|
||||
| Current_unit of 'a
|
||||
| Imported_unit of 'b
|
||||
| Not_declared
|
||||
|
||||
let get_fun_offset t closure_id =
|
||||
let fun_offset_table =
|
||||
if Closure_id.in_compilation_unit closure_id (Compilenv.current_unit ())
|
||||
then t.current_unit.fun_offset_table
|
||||
else t.imported_units.fun_offset_table
|
||||
in
|
||||
try Closure_id.Map.find closure_id fun_offset_table
|
||||
with Not_found ->
|
||||
Misc.fatal_errorf "Flambda_to_clambda: missing offset for closure %a"
|
||||
Closure_id.print closure_id
|
||||
|
||||
let get_fv_offset t var_within_closure =
|
||||
let fv_offset_table =
|
||||
if Var_within_closure.in_compilation_unit var_within_closure
|
||||
(Compilenv.current_unit ())
|
||||
then t.current_unit.fv_offset_table
|
||||
else t.imported_units.fv_offset_table
|
||||
in
|
||||
try Var_within_closure.Map.find var_within_closure fv_offset_table
|
||||
with Not_found ->
|
||||
Misc.fatal_errorf "Flambda_to_clambda: missing offset for variable %a"
|
||||
Var_within_closure.print var_within_closure
|
||||
|
||||
let function_declaration_position t closure_id =
|
||||
try
|
||||
Current_unit (Closure_id.Map.find closure_id t.current_unit.closures)
|
||||
with Not_found ->
|
||||
try
|
||||
Imported_unit (Closure_id.Map.find closure_id t.imported_units.closures)
|
||||
with Not_found -> Not_declared
|
||||
|
||||
let is_function_constant t closure_id =
|
||||
match function_declaration_position t closure_id with
|
||||
| Current_unit { set_of_closures_id } ->
|
||||
Set_of_closures_id.Set.mem set_of_closures_id
|
||||
t.current_unit.constant_sets_of_closures
|
||||
| Imported_unit { set_of_closures_id } ->
|
||||
Set_of_closures_id.Set.mem set_of_closures_id
|
||||
t.imported_units.constant_sets_of_closures
|
||||
| Not_declared ->
|
||||
Misc.fatal_errorf "Flambda_to_clambda: missing closure %a"
|
||||
Closure_id.print closure_id
|
||||
|
||||
(* Instrumentation of closure and field accesses to try to catch compiler
|
||||
bugs. *)
|
||||
|
||||
let check_closure ulam named : Clambda.ulambda =
|
||||
if not !Clflags.clambda_checks then ulam
|
||||
else
|
||||
let desc =
|
||||
Primitive.simple ~name:"caml_check_value_is_closure"
|
||||
~arity:2 ~alloc:false
|
||||
in
|
||||
let str = Format.asprintf "%a" Flambda.print_named named in
|
||||
let str_const =
|
||||
Compilenv.new_structured_constant (Uconst_string str) ~shared:true
|
||||
in
|
||||
Uprim (Pccall desc,
|
||||
[ulam; Clambda.Uconst (Uconst_ref (str_const, None))],
|
||||
Debuginfo.none)
|
||||
|
||||
let check_field ulam pos named_opt : Clambda.ulambda =
|
||||
if not !Clflags.clambda_checks then ulam
|
||||
else
|
||||
let desc =
|
||||
Primitive.simple ~name:"caml_check_field_access"
|
||||
~arity:3 ~alloc:false
|
||||
in
|
||||
let str =
|
||||
match named_opt with
|
||||
| None -> "<none>"
|
||||
| Some named -> Format.asprintf "%a" Flambda.print_named named
|
||||
in
|
||||
let str_const =
|
||||
Compilenv.new_structured_constant (Uconst_string str) ~shared:true
|
||||
in
|
||||
Uprim (Pccall desc, [ulam; Clambda.Uconst (Uconst_int pos);
|
||||
Clambda.Uconst (Uconst_ref (str_const, None))],
|
||||
Debuginfo.none)
|
||||
|
||||
module Env : sig
|
||||
type t
|
||||
|
||||
val empty : t
|
||||
|
||||
val add_subst : t -> Variable.t -> Clambda.ulambda -> t
|
||||
val find_subst_exn : t -> Variable.t -> Clambda.ulambda
|
||||
|
||||
val add_fresh_ident : t -> Variable.t -> Ident.t * t
|
||||
val ident_for_var_exn : t -> Variable.t -> Ident.t
|
||||
|
||||
val add_fresh_mutable_ident : t -> Mutable_variable.t -> Ident.t * t
|
||||
val ident_for_mutable_var_exn : t -> Mutable_variable.t -> Ident.t
|
||||
|
||||
val add_allocated_const : t -> Symbol.t -> Allocated_const.t -> t
|
||||
val allocated_const_for_symbol : t -> Symbol.t -> Allocated_const.t option
|
||||
|
||||
val keep_only_symbols : t -> t
|
||||
end = struct
|
||||
type t =
|
||||
{ subst : Clambda.ulambda Variable.Map.t;
|
||||
var : Ident.t Variable.Map.t;
|
||||
mutable_var : Ident.t Mutable_variable.Map.t;
|
||||
toplevel : bool;
|
||||
allocated_constant_for_symbol : Allocated_const.t Symbol.Map.t;
|
||||
}
|
||||
|
||||
let empty =
|
||||
{ subst = Variable.Map.empty;
|
||||
var = Variable.Map.empty;
|
||||
mutable_var = Mutable_variable.Map.empty;
|
||||
toplevel = false;
|
||||
allocated_constant_for_symbol = Symbol.Map.empty;
|
||||
}
|
||||
|
||||
let add_subst t id subst =
|
||||
{ t with subst = Variable.Map.add id subst t.subst }
|
||||
|
||||
let find_subst_exn t id = Variable.Map.find id t.subst
|
||||
|
||||
let ident_for_var_exn t id = Variable.Map.find id t.var
|
||||
|
||||
let add_fresh_ident t var =
|
||||
let id = Ident.create (Variable.unique_name var) in
|
||||
id, { t with var = Variable.Map.add var id t.var }
|
||||
|
||||
let ident_for_mutable_var_exn t mut_var =
|
||||
Mutable_variable.Map.find mut_var t.mutable_var
|
||||
|
||||
let add_fresh_mutable_ident t mut_var =
|
||||
let id = Mutable_variable.unique_ident mut_var in
|
||||
let mutable_var = Mutable_variable.Map.add mut_var id t.mutable_var in
|
||||
id, { t with mutable_var; }
|
||||
|
||||
let add_allocated_const t sym cons =
|
||||
{ t with
|
||||
allocated_constant_for_symbol =
|
||||
Symbol.Map.add sym cons t.allocated_constant_for_symbol;
|
||||
}
|
||||
|
||||
let allocated_const_for_symbol t sym =
|
||||
try
|
||||
Some (Symbol.Map.find sym t.allocated_constant_for_symbol)
|
||||
with Not_found -> None
|
||||
|
||||
let keep_only_symbols t =
|
||||
{ empty with
|
||||
allocated_constant_for_symbol = t.allocated_constant_for_symbol;
|
||||
}
|
||||
end
|
||||
|
||||
let subst_var env var : Clambda.ulambda =
|
||||
try Env.find_subst_exn env var
|
||||
with Not_found ->
|
||||
try Uvar (Env.ident_for_var_exn env var)
|
||||
with Not_found ->
|
||||
Misc.fatal_errorf "Flambda_to_clambda: unbound variable %a@."
|
||||
Variable.print var
|
||||
|
||||
let subst_vars env vars = List.map (subst_var env) vars
|
||||
|
||||
let build_uoffset ulam offset : Clambda.ulambda =
|
||||
if offset = 0 then ulam
|
||||
else Uoffset (ulam, offset)
|
||||
|
||||
let to_clambda_allocated_constant (const : Allocated_const.t)
|
||||
: Clambda.ustructured_constant =
|
||||
match const with
|
||||
| Float f -> Uconst_float f
|
||||
| Int32 i -> Uconst_int32 i
|
||||
| Int64 i -> Uconst_int64 i
|
||||
| Nativeint i -> Uconst_nativeint i
|
||||
| Immutable_string s | String s -> Uconst_string s
|
||||
| Immutable_float_array a | Float_array a -> Uconst_float_array a
|
||||
|
||||
let to_uconst_symbol env symbol : Clambda.ustructured_constant option =
|
||||
match Env.allocated_const_for_symbol env symbol with
|
||||
| Some ((Float _ | Int32 _ | Int64 _ | Nativeint _) as const) ->
|
||||
Some (to_clambda_allocated_constant const)
|
||||
| None (* CR-soon mshinwell: Try to make this an error. *)
|
||||
| Some _ -> None
|
||||
|
||||
let to_clambda_symbol' env sym : Clambda.uconstant =
|
||||
let lbl = Linkage_name.to_string (Symbol.label sym) in
|
||||
Uconst_ref (lbl, to_uconst_symbol env sym)
|
||||
|
||||
let to_clambda_symbol env sym : Clambda.ulambda =
|
||||
Uconst (to_clambda_symbol' env sym)
|
||||
|
||||
let to_clambda_const env (const : Flambda.constant_defining_value_block_field)
|
||||
: Clambda.uconstant =
|
||||
match const with
|
||||
| Symbol symbol -> to_clambda_symbol' env symbol
|
||||
| Const (Int i) -> Uconst_int i
|
||||
| Const (Char c) -> Uconst_int (Char.code c)
|
||||
| Const (Const_pointer i) -> Uconst_ptr i
|
||||
|
||||
let rec to_clambda t env (flam : Flambda.t) : Clambda.ulambda =
|
||||
match flam with
|
||||
| Var var -> subst_var env var
|
||||
| Let { var; defining_expr; body; _ } ->
|
||||
let id, env_body = Env.add_fresh_ident env var in
|
||||
Ulet (id, to_clambda_named t env var defining_expr,
|
||||
to_clambda t env_body body)
|
||||
| Let_mutable (mut_var, var, body) ->
|
||||
let id, env_body = Env.add_fresh_mutable_ident env mut_var in
|
||||
let def = subst_var env var in
|
||||
Ulet (id, def, to_clambda t env_body body)
|
||||
| Let_rec (defs, body) ->
|
||||
let env, defs =
|
||||
List.fold_right (fun (var, def) (env, defs) ->
|
||||
let id, env = Env.add_fresh_ident env var in
|
||||
env, (id, var, def) :: defs)
|
||||
defs (env, [])
|
||||
in
|
||||
let defs =
|
||||
List.map (fun (id, var, def) -> id, to_clambda_named t env var def) defs
|
||||
in
|
||||
Uletrec (defs, to_clambda t env body)
|
||||
| Apply { func; args; kind = Direct direct_func; dbg = dbg } ->
|
||||
to_clambda_direct_apply t func args direct_func dbg env
|
||||
| Apply { func; args; kind = Indirect; dbg = dbg } ->
|
||||
(* CR mshinwell for mshinwell: improve this comment *)
|
||||
(* The closure parameter of the function is added by cmmgen, but
|
||||
it already appears in the list of parameters of the clambda
|
||||
function for generic calls. Notice that for direct calls it is
|
||||
added here. *)
|
||||
let callee = subst_var env func in
|
||||
Ugeneric_apply (check_closure callee (Flambda.Expr (Var func)),
|
||||
subst_vars env args, dbg)
|
||||
| Switch (arg, sw) ->
|
||||
let aux () : Clambda.ulambda =
|
||||
let const_index, const_actions =
|
||||
to_clambda_switch t env sw.consts sw.numconsts sw.failaction
|
||||
in
|
||||
let block_index, block_actions =
|
||||
to_clambda_switch t env sw.blocks sw.numblocks sw.failaction
|
||||
in
|
||||
Uswitch (subst_var env arg,
|
||||
{ us_index_consts = const_index;
|
||||
us_actions_consts = const_actions;
|
||||
us_index_blocks = block_index;
|
||||
us_actions_blocks = block_actions;
|
||||
})
|
||||
in
|
||||
(* Check that the [failaction] may be duplicated. If this is not the
|
||||
case, share it through a static raise / static catch. *)
|
||||
(* CR-someday pchambart for pchambart: This is overly simplified. We should verify
|
||||
that this does not generates too bad code. If it the case, handle some
|
||||
let cases.
|
||||
*)
|
||||
begin match sw.failaction with
|
||||
| None -> aux ()
|
||||
| Some (Static_raise _) -> aux ()
|
||||
| Some failaction ->
|
||||
let exn = Static_exception.create () in
|
||||
let sw =
|
||||
{ sw with
|
||||
failaction = Some (Flambda.Static_raise (exn, []));
|
||||
}
|
||||
in
|
||||
let expr : Flambda.t =
|
||||
Static_catch (exn, [], Switch (arg, sw), failaction)
|
||||
in
|
||||
to_clambda t env expr
|
||||
end
|
||||
| String_switch (arg, sw, def) ->
|
||||
let arg = subst_var env arg in
|
||||
let sw = List.map (fun (s, e) -> s, to_clambda t env e) sw in
|
||||
let def = Misc.may_map (to_clambda t env) def in
|
||||
Ustringswitch (arg, sw, def)
|
||||
| Static_raise (static_exn, args) ->
|
||||
Ustaticfail (Static_exception.to_int static_exn,
|
||||
List.map (subst_var env) args)
|
||||
| Static_catch (static_exn, vars, body, handler) ->
|
||||
let env_handler, ids =
|
||||
List.fold_right (fun var (env, ids) ->
|
||||
let id, env = Env.add_fresh_ident env var in
|
||||
env, id :: ids)
|
||||
vars (env, [])
|
||||
in
|
||||
Ucatch (Static_exception.to_int static_exn, ids,
|
||||
to_clambda t env body, to_clambda t env_handler handler)
|
||||
| Try_with (body, var, handler) ->
|
||||
let id, env_handler = Env.add_fresh_ident env var in
|
||||
Utrywith (to_clambda t env body, id, to_clambda t env_handler handler)
|
||||
| If_then_else (arg, ifso, ifnot) ->
|
||||
Uifthenelse (subst_var env arg, to_clambda t env ifso,
|
||||
to_clambda t env ifnot)
|
||||
| While (cond, body) ->
|
||||
Uwhile (to_clambda t env cond, to_clambda t env body)
|
||||
| For { bound_var; from_value; to_value; direction; body } ->
|
||||
let id, env_body = Env.add_fresh_ident env bound_var in
|
||||
Ufor (id, subst_var env from_value, subst_var env to_value,
|
||||
direction, to_clambda t env_body body)
|
||||
| Assign { being_assigned; new_value } ->
|
||||
let id =
|
||||
try Env.ident_for_mutable_var_exn env being_assigned
|
||||
with Not_found ->
|
||||
Misc.fatal_errorf "Unbound mutable variable %a in [Assign]: %a"
|
||||
Mutable_variable.print being_assigned
|
||||
Flambda.print flam
|
||||
in
|
||||
Uassign (id, subst_var env new_value)
|
||||
| Send { kind; meth; obj; args; dbg } ->
|
||||
Usend (kind, subst_var env meth, subst_var env obj,
|
||||
subst_vars env args, dbg)
|
||||
| Proved_unreachable -> Uunreachable
|
||||
|
||||
and to_clambda_named t env var (named : Flambda.named) : Clambda.ulambda =
|
||||
match named with
|
||||
| Symbol sym -> to_clambda_symbol env sym
|
||||
| Const (Const_pointer n) -> Uconst (Uconst_ptr n)
|
||||
| Const (Int n) -> Uconst (Uconst_int n)
|
||||
| Const (Char c) -> Uconst (Uconst_int (Char.code c))
|
||||
| Allocated_const _ ->
|
||||
Misc.fatal_errorf "[Allocated_const] should have been lifted to a \
|
||||
[Let_symbol] construction before [Flambda_to_clambda]: %a = %a"
|
||||
Variable.print var
|
||||
Flambda.print_named named
|
||||
| Read_mutable mut_var ->
|
||||
begin try Uvar (Env.ident_for_mutable_var_exn env mut_var)
|
||||
with Not_found ->
|
||||
Misc.fatal_errorf "Unbound mutable variable %a in [Read_mutable]: %a"
|
||||
Mutable_variable.print mut_var
|
||||
Flambda.print_named named
|
||||
end
|
||||
| Read_symbol_field (symbol, field) ->
|
||||
Uprim (Pfield field, [to_clambda_symbol env symbol], Debuginfo.none)
|
||||
| Set_of_closures set_of_closures ->
|
||||
to_clambda_set_of_closures t env set_of_closures
|
||||
| Project_closure { set_of_closures; closure_id } ->
|
||||
(* CR mshinwell for pchambart: I don't understand how this comment
|
||||
relates to this code. Can you explain? *)
|
||||
(* compilation of let rec in cmmgen assumes
|
||||
that a closure is not offseted (Cmmgen.expr_size) *)
|
||||
check_closure (
|
||||
build_uoffset
|
||||
(check_closure (subst_var env set_of_closures)
|
||||
(Flambda.Expr (Var set_of_closures)))
|
||||
(get_fun_offset t closure_id))
|
||||
named
|
||||
| Move_within_set_of_closures { closure; start_from; move_to } ->
|
||||
check_closure (build_uoffset
|
||||
(check_closure (subst_var env closure)
|
||||
(Flambda.Expr (Var closure)))
|
||||
((get_fun_offset t move_to) - (get_fun_offset t start_from)))
|
||||
named
|
||||
| Project_var { closure; var; closure_id } ->
|
||||
let ulam = subst_var env closure in
|
||||
let fun_offset = get_fun_offset t closure_id in
|
||||
let var_offset = get_fv_offset t var in
|
||||
let pos = var_offset - fun_offset in
|
||||
Uprim (Pfield pos,
|
||||
[check_field (check_closure ulam (Expr (Var closure))) pos (Some named)],
|
||||
Debuginfo.none)
|
||||
| Prim (Pfield index, [block], dbg) ->
|
||||
Uprim (Pfield index, [check_field (subst_var env block) index None], dbg)
|
||||
| Prim (Psetfield (index, maybe_ptr, init), [block; new_value], dbg) ->
|
||||
Uprim (Psetfield (index, maybe_ptr, init), [
|
||||
check_field (subst_var env block) index None;
|
||||
subst_var env new_value;
|
||||
], dbg)
|
||||
| Prim (Popaque, args, dbg) ->
|
||||
Uprim (Pidentity, subst_vars env args, dbg)
|
||||
| Prim (p, args, dbg) -> Uprim (p, subst_vars env args, dbg)
|
||||
| Expr expr -> to_clambda t env expr
|
||||
|
||||
and to_clambda_switch t env cases num_keys default =
|
||||
let num_keys =
|
||||
if Numbers.Int.Set.cardinal num_keys = 0 then 0
|
||||
else Numbers.Int.Set.max_elt num_keys + 1
|
||||
in
|
||||
let index = Array.make num_keys 0 in
|
||||
let store = Flambda_utils.Switch_storer.mk_store () in
|
||||
begin match default with
|
||||
| Some def when List.length cases < num_keys -> ignore (store.act_store def)
|
||||
| _ -> ()
|
||||
end;
|
||||
List.iter (fun (key, lam) -> index.(key) <- store.act_store lam) cases;
|
||||
let actions = Array.map (to_clambda t env) (store.act_get ()) in
|
||||
match actions with
|
||||
| [| |] -> [| |], [| |] (* May happen when [default] is [None]. *)
|
||||
| _ -> index, actions
|
||||
|
||||
and to_clambda_direct_apply t func args direct_func dbg env : Clambda.ulambda =
|
||||
let closed = is_function_constant t direct_func in
|
||||
let label = Compilenv.function_label direct_func in
|
||||
let uargs =
|
||||
let uargs = subst_vars env args in
|
||||
(* CR mshinwell: improve comment. Should we check [func] too? *)
|
||||
(* If the function is closed, the function expression is always a
|
||||
variable, so it is ok to drop it. Note that it means that
|
||||
some Let can be dead. The un-anf pass should get rid of it *)
|
||||
if closed then uargs else uargs @ [subst_var env func]
|
||||
in
|
||||
Udirect_apply (label, uargs, dbg)
|
||||
|
||||
(* Describe how to build a runtime closure block that corresponds to the
|
||||
given Flambda set of closures.
|
||||
|
||||
For instance the closure for the following set of closures:
|
||||
|
||||
let rec fun_a x =
|
||||
if x <= 0 then 0 else fun_b (x-1) v1
|
||||
and fun_b x y =
|
||||
if x <= 0 then 0 else v1 + v2 + y + fun_a (x-1)
|
||||
|
||||
will be represented in memory as:
|
||||
|
||||
[ closure header; fun_a;
|
||||
1; infix header; fun caml_curry_2;
|
||||
2; fun_b; v1; v2 ]
|
||||
|
||||
fun_a and fun_b will take an additional parameter 'env' to
|
||||
access their closure. It will be arranged such that in the body
|
||||
of each function the env parameter points to its own code
|
||||
pointer. For example, in fun_b it will be shifted by 3 words.
|
||||
|
||||
Hence accessing v1 in the body of fun_a is accessing the
|
||||
6th field of 'env' and in the body of fun_b the 1st field.
|
||||
*)
|
||||
and to_clambda_set_of_closures t env
|
||||
(({ function_decls; free_vars } : Flambda.set_of_closures)
|
||||
as set_of_closures) : Clambda.ulambda =
|
||||
let all_functions = Variable.Map.bindings function_decls.funs in
|
||||
let env_var = Ident.create "env" in
|
||||
let to_clambda_function
|
||||
(closure_id, (function_decl : Flambda.function_declaration))
|
||||
: Clambda.ufunction =
|
||||
let closure_id = Closure_id.wrap closure_id in
|
||||
let fun_offset =
|
||||
Closure_id.Map.find closure_id t.current_unit.fun_offset_table
|
||||
in
|
||||
let env =
|
||||
(* Inside the body of the function, we cannot access variables
|
||||
declared outside, so start with a suitably clean environment.
|
||||
Note that we must not forget the information about which allocated
|
||||
constants contain which unboxed values. *)
|
||||
let env = Env.keep_only_symbols env in
|
||||
(* Add the Clambda expressions for the free variables of the function
|
||||
to the environment. *)
|
||||
let add_env_free_variable id _ env =
|
||||
let var_offset =
|
||||
try
|
||||
Var_within_closure.Map.find
|
||||
(Var_within_closure.wrap id) t.current_unit.fv_offset_table
|
||||
with Not_found ->
|
||||
Misc.fatal_errorf "Clambda.to_clambda_set_of_closures: offset for \
|
||||
free variable %a is unknown. Set of closures: %a"
|
||||
Variable.print id
|
||||
Flambda.print_set_of_closures set_of_closures
|
||||
in
|
||||
let pos = var_offset - fun_offset in
|
||||
Env.add_subst env id
|
||||
(Uprim (Pfield pos, [Clambda.Uvar env_var], Debuginfo.none))
|
||||
in
|
||||
let env = Variable.Map.fold add_env_free_variable free_vars env in
|
||||
(* Add the Clambda expressions for all functions defined in the current
|
||||
set of closures to the environment. The various functions may be
|
||||
retrieved by moving within the runtime closure, starting from the
|
||||
current function's closure. *)
|
||||
let add_env_function pos env (id, _) =
|
||||
let offset =
|
||||
Closure_id.Map.find (Closure_id.wrap id)
|
||||
t.current_unit.fun_offset_table
|
||||
in
|
||||
let exp : Clambda.ulambda = Uoffset (Uvar env_var, offset - pos) in
|
||||
Env.add_subst env id exp
|
||||
in
|
||||
List.fold_left (add_env_function fun_offset) env all_functions
|
||||
in
|
||||
let env_body, params =
|
||||
List.fold_right (fun var (env, params) ->
|
||||
let id, env = Env.add_fresh_ident env var in
|
||||
env, id :: params)
|
||||
function_decl.params (env, [])
|
||||
in
|
||||
{ label = Compilenv.function_label closure_id;
|
||||
arity = Flambda_utils.function_arity function_decl;
|
||||
params = params @ [env_var];
|
||||
body = to_clambda t env_body function_decl.body;
|
||||
dbg = function_decl.dbg;
|
||||
}
|
||||
in
|
||||
let funs = List.map to_clambda_function all_functions in
|
||||
let free_vars =
|
||||
Variable.Map.bindings (Variable.Map.map (subst_var env) free_vars)
|
||||
in
|
||||
Uclosure (funs, List.map snd free_vars)
|
||||
|
||||
and to_clambda_closed_set_of_closures t env symbol
|
||||
({ function_decls; } : Flambda.set_of_closures)
|
||||
: Clambda.ustructured_constant =
|
||||
let functions = Variable.Map.bindings function_decls.funs in
|
||||
let to_clambda_function (id, (function_decl : Flambda.function_declaration))
|
||||
: Clambda.ufunction =
|
||||
(* All that we need in the environment, for translating one closure from
|
||||
a closed set of closures, is the substitutions for variables bound to
|
||||
the various closures in the set. Such closures will always be
|
||||
referenced via symbols. *)
|
||||
let env =
|
||||
List.fold_left (fun env (var, _) ->
|
||||
let closure_id = Closure_id.wrap var in
|
||||
let symbol = Compilenv.closure_symbol closure_id in
|
||||
Env.add_subst env var (to_clambda_symbol env symbol))
|
||||
(Env.keep_only_symbols env)
|
||||
functions
|
||||
in
|
||||
let env_body, params =
|
||||
List.fold_right (fun var (env, params) ->
|
||||
let id, env = Env.add_fresh_ident env var in
|
||||
env, id :: params)
|
||||
function_decl.params (env, [])
|
||||
in
|
||||
{ label = Compilenv.function_label (Closure_id.wrap id);
|
||||
arity = Flambda_utils.function_arity function_decl;
|
||||
params;
|
||||
body = to_clambda t env_body function_decl.body;
|
||||
dbg = function_decl.dbg;
|
||||
}
|
||||
in
|
||||
let ufunct = List.map to_clambda_function functions in
|
||||
let closure_lbl = Linkage_name.to_string (Symbol.label symbol) in
|
||||
Uconst_closure (ufunct, closure_lbl, [])
|
||||
|
||||
let to_clambda_initialize_symbol t env symbol fields : Clambda.ulambda =
|
||||
let fields =
|
||||
List.mapi (fun index expr -> index, to_clambda t env expr) fields
|
||||
in
|
||||
let build_setfield (index, field) : Clambda.ulambda =
|
||||
(* Note that this will never cause a write barrier hit, owing to
|
||||
the [Initialization]. *)
|
||||
Uprim (Psetfield (index, Pointer, Initialization),
|
||||
[to_clambda_symbol env symbol; field],
|
||||
Debuginfo.none)
|
||||
in
|
||||
match fields with
|
||||
| [] -> Uconst (Uconst_ptr 0)
|
||||
| h :: t ->
|
||||
List.fold_left (fun acc (p, field) ->
|
||||
Clambda.Usequence (build_setfield (p, field), acc))
|
||||
(build_setfield h) t
|
||||
|
||||
let accumulate_structured_constants t env symbol
|
||||
(c : Flambda.constant_defining_value) acc =
|
||||
match c with
|
||||
| Allocated_const c ->
|
||||
Symbol.Map.add symbol (to_clambda_allocated_constant c) acc
|
||||
| Block (tag, fields) ->
|
||||
let fields = List.map (to_clambda_const env) fields in
|
||||
Symbol.Map.add symbol (Clambda.Uconst_block (Tag.to_int tag, fields)) acc
|
||||
| Set_of_closures set_of_closures ->
|
||||
let to_clambda_set_of_closures =
|
||||
to_clambda_closed_set_of_closures t env symbol set_of_closures
|
||||
in
|
||||
Symbol.Map.add symbol to_clambda_set_of_closures acc
|
||||
| Project_closure _ -> acc
|
||||
|
||||
let to_clambda_program t env constants (program : Flambda.program) =
|
||||
let rec loop env constants (program : Flambda.program_body)
|
||||
: Clambda.ulambda * Clambda.ustructured_constant Symbol.Map.t =
|
||||
match program with
|
||||
| Let_symbol (symbol, alloc, program) ->
|
||||
(* Useful only for unboxing. Since floats and boxed integers will
|
||||
never be part of a Let_rec_symbol, handling only the Let_symbol
|
||||
is sufficient. *)
|
||||
let env =
|
||||
match alloc with
|
||||
| Allocated_const const -> Env.add_allocated_const env symbol const
|
||||
| _ -> env
|
||||
in
|
||||
let constants =
|
||||
accumulate_structured_constants t env symbol alloc constants
|
||||
in
|
||||
loop env constants program
|
||||
| Let_rec_symbol (defs, program) ->
|
||||
let constants =
|
||||
List.fold_left (fun constants (symbol, alloc) ->
|
||||
accumulate_structured_constants t env symbol alloc constants)
|
||||
constants defs
|
||||
in
|
||||
loop env constants program
|
||||
| Initialize_symbol (symbol, _tag, fields, program) ->
|
||||
(* The tag is ignored here: It is used separately to generate the
|
||||
preallocated block. Only the initialisation code is generated
|
||||
here. *)
|
||||
let e1 = to_clambda_initialize_symbol t env symbol fields in
|
||||
let e2, constants = loop env constants program in
|
||||
Usequence (e1, e2), constants
|
||||
| Effect (expr, program) ->
|
||||
let e1 = to_clambda t env expr in
|
||||
let e2, constants = loop env constants program in
|
||||
Usequence (e1, e2), constants
|
||||
| End _ ->
|
||||
Uconst (Uconst_ptr 0), constants
|
||||
in
|
||||
loop env constants program.program_body
|
||||
|
||||
type result = {
|
||||
expr : Clambda.ulambda;
|
||||
preallocated_blocks : Clambda.preallocated_block list;
|
||||
structured_constants : Clambda.ustructured_constant Symbol.Map.t;
|
||||
exported : Export_info.t;
|
||||
}
|
||||
|
||||
let convert (program, exported) : result =
|
||||
let current_unit =
|
||||
let offsets = Closure_offsets.compute program in
|
||||
{ fun_offset_table = offsets.function_offsets;
|
||||
fv_offset_table = offsets.free_variable_offsets;
|
||||
closures = Flambda_utils.make_closure_map program;
|
||||
constant_sets_of_closures =
|
||||
Flambda_utils.all_lifted_constant_sets_of_closures program;
|
||||
}
|
||||
in
|
||||
let imported_units =
|
||||
let imported = Compilenv.approx_env () in
|
||||
{ fun_offset_table = imported.offset_fun;
|
||||
fv_offset_table = imported.offset_fv;
|
||||
closures = imported.closures;
|
||||
constant_sets_of_closures = imported.constant_sets_of_closures;
|
||||
}
|
||||
in
|
||||
let t = { current_unit; imported_units; } in
|
||||
let preallocated_blocks =
|
||||
List.map (fun (symbol, tag, fields) ->
|
||||
{ Clambda.
|
||||
symbol = Linkage_name.to_string (Symbol.label symbol);
|
||||
tag = Tag.to_int tag;
|
||||
size = List.length fields;
|
||||
})
|
||||
(Flambda_utils.initialize_symbols program)
|
||||
in
|
||||
let expr, structured_constants =
|
||||
to_clambda_program t Env.empty Symbol.Map.empty program
|
||||
in
|
||||
let offset_fun, offset_fv =
|
||||
Closure_offsets.compute_reexported_offsets program
|
||||
~current_unit_offset_fun:current_unit.fun_offset_table
|
||||
~current_unit_offset_fv:current_unit.fv_offset_table
|
||||
~imported_units_offset_fun:imported_units.fun_offset_table
|
||||
~imported_units_offset_fv:imported_units.fv_offset_table
|
||||
in
|
||||
let exported =
|
||||
Export_info.add_clambda_info exported
|
||||
~offset_fun
|
||||
~offset_fv
|
||||
~constant_sets_of_closures:current_unit.constant_sets_of_closures
|
||||
in
|
||||
{ expr; preallocated_blocks; structured_constants; exported; }
|
|
@ -0,0 +1,36 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
type result = {
|
||||
expr : Clambda.ulambda;
|
||||
preallocated_blocks : Clambda.preallocated_block list;
|
||||
structured_constants : Clambda.ustructured_constant Symbol.Map.t;
|
||||
exported : Export_info.t;
|
||||
}
|
||||
|
||||
(** Convert an Flambda program, with associated proto-export information,
|
||||
to Clambda.
|
||||
This yields a Clambda expression together with augmented export
|
||||
information and details about required statically-allocated values
|
||||
(preallocated blocks, for [Initialize_symbol], and structured
|
||||
constants).
|
||||
|
||||
It is during this process that accesses to variables within
|
||||
closures are transformed to field accesses within closure values.
|
||||
For direct calls, the hidden closure parameter is added. Switch
|
||||
tables are also built.
|
||||
*)
|
||||
val convert : Flambda.program * Export_info.t -> result
|
|
@ -0,0 +1,27 @@
|
|||
# Supported platforms
|
||||
|
||||
Intel and AMD x86 processors in 32-bit mode.
|
||||
The baseline is the 80486, also known as `i486`.
|
||||
(Debian's baseline is now the Pentium 1.)
|
||||
|
||||
Floating-point architecture: x87.
|
||||
(SSE2 not available in Debian's baseline.)
|
||||
|
||||
Operating systems: Linux, BSD, MacOS X, MS Windows.
|
||||
|
||||
Debian architecture name: `i386`
|
||||
|
||||
# Reference documents
|
||||
|
||||
* Instruction set architecture:
|
||||
any Intel or AMD manual of the last 20 years.
|
||||
* ELF application binary interface:
|
||||
_System V Application Binary Interface,
|
||||
Intel386 Architecture Processor Supplement_
|
||||
* MacOS X application binary interface:
|
||||
_OS X ABI Function Call Guide: IA-32 Function Calling Conventions_
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
module A = Simple_value_approx
|
||||
|
||||
let import_set_of_closures =
|
||||
let import_function_declarations (clos : Flambda.function_declarations)
|
||||
: Flambda.function_declarations =
|
||||
(* CR mshinwell for pchambart: Do we still need to do this rewriting?
|
||||
I'm wondering if maybe we don't have to any more. *)
|
||||
let sym_to_fun_var_map (clos : Flambda.function_declarations) =
|
||||
Variable.Map.fold (fun fun_var _ acc ->
|
||||
let closure_id = Closure_id.wrap fun_var in
|
||||
let sym = Compilenv.closure_symbol closure_id in
|
||||
Symbol.Map.add sym fun_var acc)
|
||||
clos.funs Symbol.Map.empty
|
||||
in
|
||||
let sym_map = sym_to_fun_var_map clos in
|
||||
let f_named (named : Flambda.named) =
|
||||
match named with
|
||||
| Symbol sym ->
|
||||
begin try Flambda.Expr (Var (Symbol.Map.find sym sym_map)) with
|
||||
| Not_found -> named
|
||||
end
|
||||
| named -> named
|
||||
in
|
||||
let funs =
|
||||
Variable.Map.map (fun (function_decl : Flambda.function_declaration) ->
|
||||
let body =
|
||||
Flambda_iterators.map_toplevel_named f_named function_decl.body
|
||||
in
|
||||
Flambda.create_function_declaration ~params:function_decl.params
|
||||
~body ~stub:function_decl.stub ~dbg:function_decl.dbg
|
||||
~inline:function_decl.inline
|
||||
~is_a_functor:function_decl.is_a_functor)
|
||||
clos.funs
|
||||
in
|
||||
Flambda.update_function_declarations clos ~funs
|
||||
in
|
||||
let aux set_of_closures_id =
|
||||
let ex_info = Compilenv.approx_env () in
|
||||
let function_declarations =
|
||||
try
|
||||
Set_of_closures_id.Map.find set_of_closures_id
|
||||
ex_info.sets_of_closures
|
||||
with Not_found ->
|
||||
Misc.fatal_errorf "[functions] does not map set of closures ID %a. \
|
||||
ex_info = %a"
|
||||
Set_of_closures_id.print set_of_closures_id
|
||||
Export_info.print_all ex_info
|
||||
in
|
||||
import_function_declarations function_declarations
|
||||
in
|
||||
Set_of_closures_id.Tbl.memoize Compilenv.imported_sets_of_closures_table aux
|
||||
|
||||
let rec import_ex ex =
|
||||
ignore (Compilenv.approx_for_global (Export_id.get_compilation_unit ex));
|
||||
let ex_info = Compilenv.approx_env () in
|
||||
let import_value_set_of_closures ~set_of_closures_id ~bound_vars
|
||||
~(ex_info : Export_info.t) ~what : A.value_set_of_closures =
|
||||
let bound_vars = Var_within_closure.Map.map import_approx bound_vars in
|
||||
match
|
||||
Set_of_closures_id.Map.find set_of_closures_id ex_info.invariant_params
|
||||
with
|
||||
| exception Not_found ->
|
||||
Misc.fatal_errorf "Set of closures ID %a not found in invariant_params \
|
||||
(when importing [%a: %s])"
|
||||
Set_of_closures_id.print set_of_closures_id
|
||||
Export_id.print ex
|
||||
what
|
||||
| invariant_params ->
|
||||
A.create_value_set_of_closures
|
||||
~function_decls:(import_set_of_closures set_of_closures_id)
|
||||
~bound_vars
|
||||
~invariant_params:(lazy invariant_params)
|
||||
~specialised_args:Variable.Map.empty
|
||||
~freshening:Freshening.Project_var.empty
|
||||
in
|
||||
match Export_info.find_description ex_info ex with
|
||||
| exception Not_found -> A.value_unknown Other
|
||||
| Value_int i -> A.value_int i
|
||||
| Value_char c -> A.value_char c
|
||||
| Value_constptr i -> A.value_constptr i
|
||||
| Value_float f -> A.value_float f
|
||||
| Value_float_array float_array ->
|
||||
begin match float_array.contents with
|
||||
| Unknown_or_mutable ->
|
||||
A.value_mutable_float_array ~size:float_array.size
|
||||
| Contents contents ->
|
||||
A.value_immutable_float_array contents
|
||||
end
|
||||
| Export_info.Value_boxed_int (t, i) -> A.value_boxed_int t i
|
||||
| Value_string { size; contents } ->
|
||||
let contents =
|
||||
match contents with
|
||||
| Unknown_or_mutable -> None
|
||||
| Contents contents -> Some contents
|
||||
in
|
||||
A.value_string size contents
|
||||
| Value_mutable_block _ -> A.value_unknown Other
|
||||
| Value_block (tag, fields) ->
|
||||
A.value_block tag (Array.map import_approx fields)
|
||||
| Value_closure { closure_id;
|
||||
set_of_closures =
|
||||
{ set_of_closures_id; bound_vars; aliased_symbol } } ->
|
||||
let value_set_of_closures =
|
||||
import_value_set_of_closures ~set_of_closures_id ~bound_vars ~ex_info
|
||||
~what:(Format.asprintf "Value_closure %a" Closure_id.print closure_id)
|
||||
in
|
||||
A.value_closure ?set_of_closures_symbol:aliased_symbol
|
||||
value_set_of_closures closure_id
|
||||
| Value_set_of_closures { set_of_closures_id; bound_vars; aliased_symbol } ->
|
||||
let value_set_of_closures =
|
||||
import_value_set_of_closures ~set_of_closures_id ~bound_vars ~ex_info
|
||||
~what:"Value_set_of_closures"
|
||||
in
|
||||
let approx = A.value_set_of_closures value_set_of_closures in
|
||||
match aliased_symbol with
|
||||
| None -> approx
|
||||
| Some symbol -> A.augment_with_symbol approx symbol
|
||||
|
||||
and import_approx (ap : Export_info.approx) =
|
||||
match ap with
|
||||
| Value_unknown -> A.value_unknown Other
|
||||
| Value_id ex -> A.value_extern ex
|
||||
| Value_symbol sym -> A.value_symbol sym
|
||||
|
||||
let import_symbol sym =
|
||||
if Compilenv.is_predefined_exception sym then
|
||||
A.value_unknown Other
|
||||
else
|
||||
let symbol_id_map =
|
||||
let global = Symbol.compilation_unit sym in
|
||||
(Compilenv.approx_for_global global).symbol_id
|
||||
in
|
||||
match Symbol.Map.find sym symbol_id_map with
|
||||
| approx -> A.augment_with_symbol (import_ex approx) sym
|
||||
| exception Not_found ->
|
||||
A.value_unresolved sym
|
||||
|
||||
(* Note for code reviewers: Observe that [really_import] iterates until
|
||||
the approximation description is fully resolved (or a necessary .cmx
|
||||
file is missing). *)
|
||||
|
||||
let rec really_import (approx : A.descr) =
|
||||
match approx with
|
||||
| Value_extern ex -> really_import_ex ex
|
||||
| Value_symbol sym -> really_import_symbol sym
|
||||
| r -> r
|
||||
|
||||
and really_import_ex ex =
|
||||
really_import (import_ex ex).descr
|
||||
|
||||
and really_import_symbol sym =
|
||||
really_import (import_symbol sym).descr
|
||||
|
||||
let really_import_approx (approx : Simple_value_approx.t) =
|
||||
A.replace_description approx (really_import approx.descr)
|
|
@ -0,0 +1,32 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(** Create simple value approximations from the export information in
|
||||
.cmx files. *)
|
||||
|
||||
(** Given an approximation description, load .cmx files (possibly more
|
||||
than one) until the description is fully resolved. If a necessary .cmx
|
||||
file cannot be found, "unresolved" will be returned. *)
|
||||
val really_import : Simple_value_approx.descr -> Simple_value_approx.descr
|
||||
|
||||
(** Maps the description of the given approximation through [really_import]. *)
|
||||
val really_import_approx : Simple_value_approx.t -> Simple_value_approx.t
|
||||
|
||||
(** Read and convert the approximation of a given symbol from the
|
||||
relevant .cmx file. Unlike the "really_" functions, this does not
|
||||
continue to load .cmx files until the approximation is fully
|
||||
resolved. *)
|
||||
val import_symbol : Symbol.t -> Simple_value_approx.t
|
|
@ -0,0 +1,27 @@
|
|||
# Supported platforms
|
||||
|
||||
IBM POWER and Freescale (nee Motorola) PowerPC processors, in three flavors:
|
||||
* 32 bits, ELF ABI: Debian's `powerpc`
|
||||
* 64 bits big-endian, ELF ABI v1: Debian's `powerpc`
|
||||
* 64 bits little-endian, ELF ABI v2: Debian's `ppc64el`
|
||||
|
||||
No longer supported: AIX and MacOS X.
|
||||
|
||||
# Reference documents
|
||||
|
||||
* Instruction set architecture:
|
||||
_PowerPC User Instruction Set Architecture_,
|
||||
book 1 of _PowerPC Architecture Book_
|
||||
(http://www.ibm.com/developerworks/systems/library/es-archguide-v2.html).
|
||||
* ELF ABI 32 bits:
|
||||
_System V Application Binary Interface, PowerPC Processor Supplement_
|
||||
* ELF ABI 64 bits version 1:
|
||||
_64-bit PowerPC ELF Application Binary Interface Supplement_
|
||||
(http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html)
|
||||
* ELF ABI 64 bits version 2:
|
||||
_Power Architecture 64-bit ELF V2 ABI Specification,
|
||||
OpenPOWER ABI for Linux Supplement_
|
||||
(http://openpowerfoundation.org/technical/technical-resources/technical-specifications/)
|
||||
* _The PowerPC Compiler Writer's Guide_, Warthman Associates, 1996.
|
||||
(PDF available from various sources on the Web.)
|
||||
|
|
@ -31,14 +31,27 @@ let rec structured_constant ppf = function
|
|||
List.iter (fun f -> fprintf ppf ",%F" f) fl;
|
||||
fprintf ppf ")"
|
||||
| Uconst_string s -> fprintf ppf "%S" s
|
||||
| Uconst_closure(clos, sym, fv) ->
|
||||
let idents ppf =
|
||||
List.iter (fprintf ppf "@ %a" Ident.print)in
|
||||
let one_fun ppf f =
|
||||
fprintf ppf "(fun@ %s@ %d@ @[<2>%a@]@ @[<2>%a@])"
|
||||
f.label f.arity idents f.params lam f.body in
|
||||
let funs ppf =
|
||||
List.iter (fprintf ppf "@ %a" one_fun) in
|
||||
let sconsts ppf scl =
|
||||
List.iter (fun sc -> fprintf ppf "@ %a" uconstant sc) scl in
|
||||
fprintf ppf "@[<2>(const_closure%a %s@ %a)@]" funs clos sym sconsts fv
|
||||
|
||||
|
||||
and uconstant ppf = function
|
||||
| Uconst_ref (s, c) ->
|
||||
| Uconst_ref (s, Some c) ->
|
||||
fprintf ppf "%S=%a" s structured_constant c
|
||||
| Uconst_ref (s, None) -> fprintf ppf "%S"s
|
||||
| Uconst_int i -> fprintf ppf "%i" i
|
||||
| Uconst_ptr i -> fprintf ppf "%ia" i
|
||||
|
||||
let rec lam ppf = function
|
||||
and lam ppf = function
|
||||
| Uvar id ->
|
||||
Ident.print ppf id
|
||||
| Uconst c -> uconstant ppf c
|
||||
|
@ -157,6 +170,8 @@ let rec lam ppf = function
|
|||
else if k = Lambda.Cached then "cache"
|
||||
else "" in
|
||||
fprintf ppf "@[<2>(send%s@ %a@ %a%a)@]" kind lam obj lam met args largs
|
||||
| Uunreachable ->
|
||||
fprintf ppf "unreachable"
|
||||
|
||||
and sequence ppf ulam = match ulam with
|
||||
| Usequence(l1, l2) ->
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# Supported platforms
|
||||
|
||||
IBM z Systems version 10 and up, in 64-bit flat addressing mode,
|
||||
running Linux (Debian architecture: `s390x`).
|
||||
|
||||
# Reference documents
|
||||
|
||||
* Instruction set architecture:
|
||||
_z/Architecture Principles of Operation_,
|
||||
SA22-7832-07, eight edition (Feb 2009).
|
||||
This is the version that corresponds to z10.
|
||||
Newer versions of this manual include additional instructions
|
||||
that are not in z10.
|
||||
* ELF ABI:
|
||||
_zSeries ELF Application Binary Interface Supplement_
|
||||
(http://refspecs.linuxfoundation.org/ELF/zSeries/index.html)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Supported platforms
|
||||
|
||||
SPARC v8 and up, in 32-bit mode.
|
||||
|
||||
Operating systems: Solaris, Linux
|
||||
(abandoned since major Linux distributions no longer support SPARC).
|
||||
|
||||
Status of this port: nearly abandoned
|
||||
(no hardware or virtual machine available for testing).
|
||||
|
||||
# Reference documents
|
||||
|
||||
* Instruction set architecture:
|
||||
_The SPARC Architecture Manual_ version 8.
|
||||
* ELF application binary interface:
|
||||
_System V Application Binary Interface,
|
||||
SPARC Processor Supplement_
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,750 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(* We say that an [Ident.t] is "linear" iff:
|
||||
(a) it is used exactly once;
|
||||
(b) it is never assigned to (using [Uassign]).
|
||||
*)
|
||||
type ident_info =
|
||||
{ used : Ident.Set.t;
|
||||
linear : Ident.Set.t;
|
||||
assigned : Ident.Set.t;
|
||||
closure_environment : Ident.Set.t;
|
||||
let_bound_vars_that_can_be_moved : Ident.Set.t;
|
||||
}
|
||||
|
||||
let ignore_uconstant (_ : Clambda.uconstant) = ()
|
||||
let ignore_ulambda (_ : Clambda.ulambda) = ()
|
||||
let ignore_ulambda_list (_ : Clambda.ulambda list) = ()
|
||||
let ignore_function_label (_ : Clambda.function_label) = ()
|
||||
let ignore_debuginfo (_ : Debuginfo.t) = ()
|
||||
let ignore_int (_ : int) = ()
|
||||
let ignore_ident (_ : Ident.t) = ()
|
||||
let ignore_primitive (_ : Lambda.primitive) = ()
|
||||
let ignore_string (_ : string) = ()
|
||||
let ignore_int_array (_ : int array) = ()
|
||||
let ignore_ident_list (_ : Ident.t list) = ()
|
||||
let ignore_direction_flag (_ : Asttypes.direction_flag) = ()
|
||||
let ignore_meth_kind (_ : Lambda.meth_kind) = ()
|
||||
|
||||
(* CR-soon mshinwell: check we aren't traversing function bodies more than
|
||||
once (need to analyse exactly what the calls are from Cmmgen into this
|
||||
module). *)
|
||||
|
||||
let closure_environment_ident (ufunction:Clambda.ufunction) =
|
||||
(* The argument after the arity is the environment *)
|
||||
if List.length ufunction.params = ufunction.arity + 1 then
|
||||
let env_var = List.nth ufunction.params ufunction.arity in
|
||||
assert(Ident.name env_var = "env");
|
||||
Some env_var
|
||||
else
|
||||
(* closed function, no environment *)
|
||||
None
|
||||
|
||||
let make_ident_info (clam : Clambda.ulambda) : ident_info =
|
||||
let t : int Ident.Tbl.t = Ident.Tbl.create 42 in
|
||||
let assigned_idents = ref Ident.Set.empty in
|
||||
let environment_idents = ref Ident.Set.empty in
|
||||
let rec loop : Clambda.ulambda -> unit = function
|
||||
(* No underscores in the pattern match, to reduce the chance of failing
|
||||
to traverse some subexpression. *)
|
||||
| Uvar id ->
|
||||
begin match Ident.Tbl.find t id with
|
||||
| n -> Ident.Tbl.replace t id (n + 1)
|
||||
| exception Not_found -> Ident.Tbl.add t id 1
|
||||
end
|
||||
| Uconst const ->
|
||||
(* The only variables that might occur in [const] are those in constant
|
||||
closures---and those are all bound by such closures. It follows that
|
||||
[const] cannot contain any variables that are bound in the current
|
||||
scope, so we do not need to count them here. (The function bodies
|
||||
of the closures will be traversed when this function is called from
|
||||
[Cmmgen.transl_function].) *)
|
||||
ignore_uconstant const
|
||||
| Udirect_apply (label, args, dbg) ->
|
||||
ignore_function_label label;
|
||||
List.iter loop args;
|
||||
ignore_debuginfo dbg
|
||||
| Ugeneric_apply (func, args, dbg) ->
|
||||
loop func;
|
||||
List.iter loop args;
|
||||
ignore_debuginfo dbg
|
||||
| Uclosure (functions, captured_variables) ->
|
||||
List.iter loop captured_variables;
|
||||
List.iter (fun ({ Clambda. label; arity; params; body; dbg } as clos) ->
|
||||
(match closure_environment_ident clos with
|
||||
| None -> ()
|
||||
| Some env_var ->
|
||||
environment_idents :=
|
||||
Ident.Set.add env_var !environment_idents);
|
||||
ignore_function_label label;
|
||||
ignore_int arity;
|
||||
ignore_ident_list params;
|
||||
loop body;
|
||||
ignore_debuginfo dbg)
|
||||
functions
|
||||
| Uoffset (expr, offset) ->
|
||||
loop expr;
|
||||
ignore_int offset
|
||||
| Ulet (ident, def, body) ->
|
||||
ignore ident;
|
||||
loop def;
|
||||
loop body
|
||||
| Uletrec (defs, body) ->
|
||||
List.iter (fun (ident, def) ->
|
||||
ignore_ident ident;
|
||||
loop def)
|
||||
defs;
|
||||
loop body
|
||||
| Uprim (prim, args, dbg) ->
|
||||
ignore_primitive prim;
|
||||
List.iter loop args;
|
||||
ignore_debuginfo dbg
|
||||
| Uswitch (cond, { us_index_consts; us_actions_consts;
|
||||
us_index_blocks; us_actions_blocks }) ->
|
||||
loop cond;
|
||||
ignore_int_array us_index_consts;
|
||||
Array.iter loop us_actions_consts;
|
||||
ignore_int_array us_index_blocks;
|
||||
Array.iter loop us_actions_blocks
|
||||
| Ustringswitch (cond, branches, default) ->
|
||||
loop cond;
|
||||
List.iter (fun (str, branch) ->
|
||||
ignore_string str;
|
||||
loop branch)
|
||||
branches;
|
||||
Misc.may loop default
|
||||
| Ustaticfail (static_exn, args) ->
|
||||
ignore_int static_exn;
|
||||
List.iter loop args
|
||||
| Ucatch (static_exn, idents, body, handler) ->
|
||||
ignore_int static_exn;
|
||||
ignore_ident_list idents;
|
||||
loop body;
|
||||
loop handler
|
||||
| Utrywith (body, ident, handler) ->
|
||||
loop body;
|
||||
ignore_ident ident;
|
||||
loop handler
|
||||
| Uifthenelse (cond, ifso, ifnot) ->
|
||||
loop cond;
|
||||
loop ifso;
|
||||
loop ifnot
|
||||
| Usequence (e1, e2) ->
|
||||
loop e1;
|
||||
loop e2
|
||||
| Uwhile (cond, body) ->
|
||||
loop cond;
|
||||
loop body
|
||||
| Ufor (ident, low, high, direction_flag, body) ->
|
||||
ignore_ident ident;
|
||||
loop low;
|
||||
loop high;
|
||||
ignore_direction_flag direction_flag;
|
||||
loop body
|
||||
| Uassign (ident, expr) ->
|
||||
assigned_idents := Ident.Set.add ident !assigned_idents;
|
||||
loop expr
|
||||
| Usend (meth_kind, e1, e2, args, dbg) ->
|
||||
ignore_meth_kind meth_kind;
|
||||
loop e1;
|
||||
loop e2;
|
||||
List.iter loop args;
|
||||
ignore_debuginfo dbg
|
||||
| Uunreachable ->
|
||||
()
|
||||
in
|
||||
loop clam;
|
||||
let linear =
|
||||
Ident.Tbl.fold (fun id n acc ->
|
||||
assert (n >= 1);
|
||||
if n = 1 && not (Ident.Set.mem id !assigned_idents)
|
||||
then Ident.Set.add id acc
|
||||
else acc)
|
||||
t Ident.Set.empty
|
||||
in
|
||||
let assigned = !assigned_idents in
|
||||
let used =
|
||||
(* This doesn't work transitively and thus is somewhat restricted. In
|
||||
particular, it does not allow us to get rid of useless chains of [let]s.
|
||||
However it should be sufficient to remove the majority of unnecessary
|
||||
[let] bindings that might hinder [Cmmgen]. *)
|
||||
Ident.Tbl.fold (fun id _n acc -> Ident.Set.add id acc)
|
||||
t assigned
|
||||
in
|
||||
{ used; linear; assigned; closure_environment = !environment_idents;
|
||||
let_bound_vars_that_can_be_moved = Ident.Set.empty;
|
||||
}
|
||||
|
||||
(* When sequences of [let]-bindings match the evaluation order in a subsequent
|
||||
primitive or function application whose arguments are linearly-used
|
||||
non-assigned variables bound by such lets (possibly interspersed with other
|
||||
variables that are known to be constant), and it is known that there were no
|
||||
intervening side-effects during the evaluation of the [let]-bindings,
|
||||
permit substitution of the variables for their defining expressions. *)
|
||||
let let_bound_vars_that_can_be_moved ident_info (clam : Clambda.ulambda) =
|
||||
let obviously_constant = ref Ident.Set.empty in
|
||||
let can_move = ref Ident.Set.empty in
|
||||
let let_stack = ref [] in
|
||||
let examine_argument_list args =
|
||||
let rec loop let_bound_vars (args : Clambda.ulambda list) =
|
||||
match let_bound_vars, args with
|
||||
| _, [] ->
|
||||
(* We've matched all arguments and will not substitute (in the
|
||||
current application being considered) any of the remaining
|
||||
[let_bound_vars]. As such they may stay on the stack. *)
|
||||
let_bound_vars
|
||||
| [], _ ->
|
||||
(* There are no more [let]-bindings to consider, so the stack
|
||||
is left empty. *)
|
||||
[]
|
||||
| let_bound_vars, (Uvar arg)::args
|
||||
when Ident.Set.mem arg !obviously_constant ->
|
||||
loop let_bound_vars args
|
||||
| let_bound_var::let_bound_vars, (Uvar arg)::args
|
||||
when Ident.same let_bound_var arg
|
||||
&& not (Ident.Set.mem arg ident_info.assigned) ->
|
||||
assert (Ident.Set.mem arg ident_info.used);
|
||||
assert (Ident.Set.mem arg ident_info.linear);
|
||||
can_move := Ident.Set.add arg !can_move;
|
||||
loop let_bound_vars args
|
||||
| _::_, _::_ ->
|
||||
(* The [let] sequence has ceased to match the evaluation order
|
||||
or we have encountered some complicated argument. In this case
|
||||
we empty the stack to ensure that we do not end up moving an
|
||||
outer [let] across a side effect. *)
|
||||
[]
|
||||
in
|
||||
(* Start at the most recent let binding and the leftmost argument
|
||||
(the last argument to be evaluated). *)
|
||||
let_stack := loop !let_stack args
|
||||
in
|
||||
let rec loop : Clambda.ulambda -> unit = function
|
||||
| Uvar ident ->
|
||||
if Ident.Set.mem ident ident_info.assigned then begin
|
||||
let_stack := []
|
||||
end
|
||||
| Uconst const ->
|
||||
ignore_uconstant const
|
||||
| Udirect_apply (label, args, dbg) ->
|
||||
ignore_function_label label;
|
||||
examine_argument_list args;
|
||||
(* We don't currently traverse [args]; they should all be variables
|
||||
anyway. If this is added in the future, take care to traverse [args]
|
||||
following the evaluation order. *)
|
||||
ignore_debuginfo dbg
|
||||
| Ugeneric_apply (func, args, dbg) ->
|
||||
examine_argument_list (args @ [func]);
|
||||
ignore_debuginfo dbg
|
||||
| Uclosure (functions, captured_variables) ->
|
||||
ignore_ulambda_list captured_variables;
|
||||
(* Start a new let stack for speed. *)
|
||||
List.iter (fun { Clambda. label; arity; params; body; dbg; } ->
|
||||
ignore_function_label label;
|
||||
ignore_int arity;
|
||||
ignore_ident_list params;
|
||||
let_stack := [];
|
||||
loop body;
|
||||
let_stack := [];
|
||||
ignore_debuginfo dbg)
|
||||
functions
|
||||
| Uoffset (expr, offset) ->
|
||||
(* [expr] should usually be a variable. *)
|
||||
examine_argument_list [expr];
|
||||
ignore_int offset
|
||||
| Ulet (ident, def, body) ->
|
||||
begin match def with
|
||||
| Uconst _ ->
|
||||
(* The defining expression is obviously constant, so we don't
|
||||
have to put this [let] on the stack, and we don't have to
|
||||
traverse the defining expression either. *)
|
||||
obviously_constant := Ident.Set.add ident !obviously_constant;
|
||||
loop body
|
||||
| _ ->
|
||||
loop def;
|
||||
if Ident.Set.mem ident ident_info.linear then begin
|
||||
let_stack := ident::!let_stack
|
||||
end else begin
|
||||
(* If we encounter a non-linear [let]-binding then we must clear
|
||||
the let stack, since we cannot now move any previous binding
|
||||
across the non-linear one. *)
|
||||
let_stack := []
|
||||
end;
|
||||
loop body
|
||||
end
|
||||
| Uletrec (defs, body) ->
|
||||
(* Evaluation order for [defs] is not defined, and this case
|
||||
probably isn't important for [Cmmgen] anyway. *)
|
||||
let_stack := [];
|
||||
List.iter (fun (ident, def) ->
|
||||
ignore_ident ident;
|
||||
loop def;
|
||||
let_stack := [])
|
||||
defs;
|
||||
loop body
|
||||
| Uprim (prim, args, dbg) ->
|
||||
ignore_primitive prim;
|
||||
examine_argument_list args;
|
||||
ignore_debuginfo dbg
|
||||
| Uswitch (cond, { us_index_consts; us_actions_consts;
|
||||
us_index_blocks; us_actions_blocks }) ->
|
||||
examine_argument_list [cond];
|
||||
ignore_int_array us_index_consts;
|
||||
Array.iter (fun action ->
|
||||
let_stack := [];
|
||||
loop action)
|
||||
us_actions_consts;
|
||||
ignore_int_array us_index_blocks;
|
||||
Array.iter (fun action ->
|
||||
let_stack := [];
|
||||
loop action)
|
||||
us_actions_blocks;
|
||||
let_stack := []
|
||||
| Ustringswitch (cond, branches, default) ->
|
||||
examine_argument_list [cond];
|
||||
List.iter (fun (str, branch) ->
|
||||
ignore_string str;
|
||||
let_stack := [];
|
||||
loop branch)
|
||||
branches;
|
||||
let_stack := [];
|
||||
Misc.may loop default;
|
||||
let_stack := []
|
||||
| Ustaticfail (static_exn, args) ->
|
||||
ignore_int static_exn;
|
||||
ignore_ulambda_list args;
|
||||
let_stack := []
|
||||
| Ucatch (static_exn, idents, body, handler) ->
|
||||
ignore_int static_exn;
|
||||
ignore_ident_list idents;
|
||||
let_stack := [];
|
||||
loop body;
|
||||
let_stack := [];
|
||||
loop handler;
|
||||
let_stack := []
|
||||
| Utrywith (body, ident, handler) ->
|
||||
let_stack := [];
|
||||
loop body;
|
||||
let_stack := [];
|
||||
ignore_ident ident;
|
||||
loop handler;
|
||||
let_stack := []
|
||||
| Uifthenelse (cond, ifso, ifnot) ->
|
||||
examine_argument_list [cond];
|
||||
let_stack := [];
|
||||
loop ifso;
|
||||
let_stack := [];
|
||||
loop ifnot;
|
||||
let_stack := []
|
||||
| Usequence (e1, e2) ->
|
||||
loop e1;
|
||||
let_stack := [];
|
||||
loop e2;
|
||||
let_stack := []
|
||||
| Uwhile (cond, body) ->
|
||||
let_stack := [];
|
||||
loop cond;
|
||||
let_stack := [];
|
||||
loop body;
|
||||
let_stack := []
|
||||
| Ufor (ident, low, high, direction_flag, body) ->
|
||||
ignore_ident ident;
|
||||
(* Cmmgen generates code that evaluates low before high,
|
||||
but we don't do anything here at the moment anyway. *)
|
||||
ignore_ulambda low;
|
||||
ignore_ulambda high;
|
||||
ignore_direction_flag direction_flag;
|
||||
let_stack := [];
|
||||
loop body;
|
||||
let_stack := []
|
||||
| Uassign (ident, expr) ->
|
||||
ignore_ident ident;
|
||||
ignore_ulambda expr;
|
||||
let_stack := []
|
||||
| Usend (meth_kind, e1, e2, args, dbg) ->
|
||||
ignore_meth_kind meth_kind;
|
||||
ignore_ulambda e1;
|
||||
ignore_ulambda e2;
|
||||
ignore_ulambda_list args;
|
||||
let_stack := [];
|
||||
ignore_debuginfo dbg
|
||||
| Uunreachable ->
|
||||
let_stack := []
|
||||
in
|
||||
loop clam;
|
||||
!can_move
|
||||
|
||||
(* Substitution of an expression for a let-moveable variable can cause the
|
||||
surrounding expression to become fixed. To avoid confusion, do the
|
||||
let-moveable substitutions first. *)
|
||||
let rec substitute_let_moveable is_let_moveable env (clam : Clambda.ulambda)
|
||||
: Clambda.ulambda =
|
||||
match clam with
|
||||
| Uvar id ->
|
||||
if not (Ident.Set.mem id is_let_moveable) then
|
||||
clam
|
||||
else
|
||||
begin match Ident.Map.find id env with
|
||||
| clam -> clam
|
||||
| exception Not_found ->
|
||||
Misc.fatal_errorf "substitute_let_moveable: Unbound identifier %a"
|
||||
Ident.print id
|
||||
end
|
||||
| Uconst _ -> clam
|
||||
| Udirect_apply (label, args, dbg) ->
|
||||
let args = substitute_let_moveable_list is_let_moveable env args in
|
||||
Udirect_apply (label, args, dbg)
|
||||
| Ugeneric_apply (func, args, dbg) ->
|
||||
let func = substitute_let_moveable is_let_moveable env func in
|
||||
let args = substitute_let_moveable_list is_let_moveable env args in
|
||||
Ugeneric_apply (func, args, dbg)
|
||||
| Uclosure (functions, variables_bound_by_the_closure) ->
|
||||
let functions =
|
||||
List.map (fun (ufunction : Clambda.ufunction) ->
|
||||
{ ufunction with
|
||||
body = substitute_let_moveable is_let_moveable env ufunction.body;
|
||||
})
|
||||
functions
|
||||
in
|
||||
let variables_bound_by_the_closure =
|
||||
substitute_let_moveable_list is_let_moveable env variables_bound_by_the_closure
|
||||
in
|
||||
Uclosure (functions, variables_bound_by_the_closure)
|
||||
| Uoffset (clam, n) ->
|
||||
let clam = substitute_let_moveable is_let_moveable env clam in
|
||||
Uoffset (clam, n)
|
||||
| Ulet (id, def, body) ->
|
||||
let def = substitute_let_moveable is_let_moveable env def in
|
||||
if Ident.Set.mem id is_let_moveable then
|
||||
let env = Ident.Map.add id def env in
|
||||
substitute_let_moveable is_let_moveable env body
|
||||
else
|
||||
Ulet (id, def, substitute_let_moveable is_let_moveable env body)
|
||||
| Uletrec (defs, body) ->
|
||||
let defs =
|
||||
List.map (fun (id, def) ->
|
||||
id, substitute_let_moveable is_let_moveable env def)
|
||||
defs
|
||||
in
|
||||
let body = substitute_let_moveable is_let_moveable env body in
|
||||
Uletrec (defs, body)
|
||||
| Uprim (prim, args, dbg) ->
|
||||
let args = substitute_let_moveable_list is_let_moveable env args in
|
||||
Uprim (prim, args, dbg)
|
||||
| Uswitch (cond, sw) ->
|
||||
let cond = substitute_let_moveable is_let_moveable env cond in
|
||||
let sw =
|
||||
{ sw with
|
||||
us_actions_consts = substitute_let_moveable_array is_let_moveable env sw.us_actions_consts;
|
||||
us_actions_blocks = substitute_let_moveable_array is_let_moveable env sw.us_actions_blocks;
|
||||
}
|
||||
in
|
||||
Uswitch (cond, sw)
|
||||
| Ustringswitch (cond, branches, default) ->
|
||||
let cond = substitute_let_moveable is_let_moveable env cond in
|
||||
let branches =
|
||||
List.map (fun (s, branch) -> s, substitute_let_moveable is_let_moveable env branch)
|
||||
branches
|
||||
in
|
||||
let default = Misc.may_map (substitute_let_moveable is_let_moveable env) default in
|
||||
Ustringswitch (cond, branches, default)
|
||||
| Ustaticfail (n, args) ->
|
||||
let args = substitute_let_moveable_list is_let_moveable env args in
|
||||
Ustaticfail (n, args)
|
||||
| Ucatch (n, ids, body, handler) ->
|
||||
let body = substitute_let_moveable is_let_moveable env body in
|
||||
let handler = substitute_let_moveable is_let_moveable env handler in
|
||||
Ucatch (n, ids, body, handler)
|
||||
| Utrywith (body, id, handler) ->
|
||||
let body = substitute_let_moveable is_let_moveable env body in
|
||||
let handler = substitute_let_moveable is_let_moveable env handler in
|
||||
Utrywith (body, id, handler)
|
||||
| Uifthenelse (cond, ifso, ifnot) ->
|
||||
let cond = substitute_let_moveable is_let_moveable env cond in
|
||||
let ifso = substitute_let_moveable is_let_moveable env ifso in
|
||||
let ifnot = substitute_let_moveable is_let_moveable env ifnot in
|
||||
Uifthenelse (cond, ifso, ifnot)
|
||||
| Usequence (e1, e2) ->
|
||||
let e1 = substitute_let_moveable is_let_moveable env e1 in
|
||||
let e2 = substitute_let_moveable is_let_moveable env e2 in
|
||||
Usequence (e1, e2)
|
||||
| Uwhile (cond, body) ->
|
||||
let cond = substitute_let_moveable is_let_moveable env cond in
|
||||
let body = substitute_let_moveable is_let_moveable env body in
|
||||
Uwhile (cond, body)
|
||||
| Ufor (id, low, high, direction, body) ->
|
||||
let low = substitute_let_moveable is_let_moveable env low in
|
||||
let high = substitute_let_moveable is_let_moveable env high in
|
||||
let body = substitute_let_moveable is_let_moveable env body in
|
||||
Ufor (id, low, high, direction, body)
|
||||
| Uassign (id, expr) ->
|
||||
let expr = substitute_let_moveable is_let_moveable env expr in
|
||||
Uassign (id, expr)
|
||||
| Usend (kind, e1, e2, args, dbg) ->
|
||||
let e1 = substitute_let_moveable is_let_moveable env e1 in
|
||||
let e2 = substitute_let_moveable is_let_moveable env e2 in
|
||||
let args = substitute_let_moveable_list is_let_moveable env args in
|
||||
Usend (kind, e1, e2, args, dbg)
|
||||
| Uunreachable ->
|
||||
Uunreachable
|
||||
|
||||
and substitute_let_moveable_list is_let_moveable env clams =
|
||||
List.map (substitute_let_moveable is_let_moveable env) clams
|
||||
|
||||
and substitute_let_moveable_array is_let_moveable env clams =
|
||||
Array.map (substitute_let_moveable is_let_moveable env) clams
|
||||
|
||||
(* We say that an expression is "moveable" iff it has neither effects nor
|
||||
coeffects. (See semantics_of_primitives.mli.)
|
||||
*)
|
||||
type moveable = Fixed | Moveable | Moveable_not_into_loops
|
||||
|
||||
let both_moveable a b =
|
||||
match a, b with
|
||||
| Moveable, Moveable -> Moveable
|
||||
| Moveable_not_into_loops, Moveable
|
||||
| Moveable, Moveable_not_into_loops
|
||||
| Moveable_not_into_loops, Moveable_not_into_loops -> Moveable_not_into_loops
|
||||
| Moveable, Fixed
|
||||
| Moveable_not_into_loops, Fixed
|
||||
| Fixed, Moveable_not_into_loops
|
||||
| Fixed, Moveable
|
||||
| Fixed, Fixed -> Fixed
|
||||
|
||||
let primitive_moveable (prim : Lambda.primitive)
|
||||
(args : Clambda.ulambda list)
|
||||
(ident_info : ident_info) =
|
||||
match prim, args with
|
||||
| Pfield _, [Uconst (Uconst_ref (_, _))] ->
|
||||
(* CR mshinwell: Actually, maybe this shouldn't be needed; these should
|
||||
have been simplified to [Read_symbol_field], which doesn't yield a
|
||||
Clambda let. This might be fixed when Inline_and_simplify can
|
||||
turn Pfield into Read_symbol_field. *)
|
||||
(* Allow field access of symbols to be moveable. (The comment in
|
||||
flambda.mli on [Read_symbol_field] may be helpful to the reader.) *)
|
||||
Moveable
|
||||
| Pfield _, [Uvar id] when Ident.Set.mem id ident_info.closure_environment ->
|
||||
(* accesses to the function environment is coeffect free: this block
|
||||
is never mutated *)
|
||||
Moveable
|
||||
| _ ->
|
||||
match Semantics_of_primitives.for_primitive prim with
|
||||
| No_effects, No_coeffects -> Moveable
|
||||
| No_effects, Has_coeffects
|
||||
| Only_generative_effects, No_coeffects
|
||||
| Only_generative_effects, Has_coeffects
|
||||
| Arbitrary_effects, No_coeffects
|
||||
| Arbitrary_effects, Has_coeffects -> Fixed
|
||||
|
||||
type moveable_for_env = Moveable | Moveable_not_into_loops
|
||||
|
||||
(** Called when we are entering a loop or body of a function (which may be
|
||||
called multiple times). The environment is rewritten such that
|
||||
identifiers previously moveable, but not into loops, are now fixed. *)
|
||||
let going_into_loop env =
|
||||
Ident.Map.filter_map env ~f:(fun _var ((moveable : moveable_for_env), def) ->
|
||||
match moveable with
|
||||
| Moveable -> Some (Moveable, def)
|
||||
| Moveable_not_into_loops -> None)
|
||||
|
||||
(** Eliminate, through substitution, [let]-bindings of linear variables with
|
||||
moveable defining expressions. *)
|
||||
let rec un_anf_and_moveable ident_info env (clam : Clambda.ulambda)
|
||||
: Clambda.ulambda * moveable =
|
||||
match clam with
|
||||
| Uvar id ->
|
||||
begin match Ident.Map.find id env with
|
||||
| Moveable, def -> def, Moveable
|
||||
| Moveable_not_into_loops, def -> def, Moveable_not_into_loops
|
||||
| exception Not_found ->
|
||||
let moveable : moveable =
|
||||
if Ident.Set.mem id ident_info.assigned then
|
||||
Fixed
|
||||
else
|
||||
Moveable
|
||||
in
|
||||
clam, moveable
|
||||
end
|
||||
| Uconst _ ->
|
||||
(* Constant closures are rewritten separately. *)
|
||||
clam, Moveable
|
||||
| Udirect_apply (label, args, dbg) ->
|
||||
let args = un_anf_list ident_info env args in
|
||||
Udirect_apply (label, args, dbg), Fixed
|
||||
| Ugeneric_apply (func, args, dbg) ->
|
||||
let func = un_anf ident_info env func in
|
||||
let args = un_anf_list ident_info env args in
|
||||
Ugeneric_apply (func, args, dbg), Fixed
|
||||
| Uclosure (functions, variables_bound_by_the_closure) ->
|
||||
let functions =
|
||||
List.map (fun (ufunction : Clambda.ufunction) ->
|
||||
{ ufunction with
|
||||
body = un_anf ident_info (going_into_loop env) ufunction.body;
|
||||
})
|
||||
functions
|
||||
in
|
||||
let variables_bound_by_the_closure, moveable =
|
||||
un_anf_list_and_moveable ident_info env variables_bound_by_the_closure
|
||||
in
|
||||
Uclosure (functions, variables_bound_by_the_closure),
|
||||
both_moveable moveable Moveable_not_into_loops
|
||||
| Uoffset (clam, n) ->
|
||||
let clam, moveable = un_anf_and_moveable ident_info env clam in
|
||||
Uoffset (clam, n), moveable
|
||||
| Ulet (id, def, Uvar id') when Ident.same id id' ->
|
||||
un_anf_and_moveable ident_info env def
|
||||
| Ulet (id, def, body) ->
|
||||
let def, def_moveable = un_anf_and_moveable ident_info env def in
|
||||
let is_linear = Ident.Set.mem id ident_info.linear in
|
||||
let is_used = Ident.Set.mem id ident_info.used in
|
||||
begin match def_moveable, is_linear, is_used with
|
||||
| (Moveable | Moveable_not_into_loops), _, false ->
|
||||
(* A moveable expression that is never used may be eliminated. *)
|
||||
un_anf_and_moveable ident_info env body
|
||||
| Moveable, true, true ->
|
||||
(* A moveable expression bound to a linear [Ident.t] may replace the
|
||||
single occurrence of the identifier. *)
|
||||
let env =
|
||||
let def_moveable : moveable_for_env =
|
||||
match def_moveable with
|
||||
| Moveable -> Moveable
|
||||
| Moveable_not_into_loops -> Moveable_not_into_loops
|
||||
| Fixed -> assert false
|
||||
in
|
||||
Ident.Map.add id (def_moveable, def) env
|
||||
in
|
||||
un_anf_and_moveable ident_info env body
|
||||
| Moveable_not_into_loops, true, true
|
||||
(* We can't delete the [let] binding in this case because we don't
|
||||
know whether the variable was substituted for its definition
|
||||
(in the case of its linear use not being inside a loop) or not.
|
||||
We could extend the code to cope with this case. *)
|
||||
| (Moveable | Moveable_not_into_loops), false, true
|
||||
(* Moveable but not used linearly. *)
|
||||
| Fixed, _, _ ->
|
||||
let body, body_moveable = un_anf_and_moveable ident_info env body in
|
||||
Ulet (id, def, body), both_moveable def_moveable body_moveable
|
||||
end
|
||||
| Uletrec (defs, body) ->
|
||||
let defs =
|
||||
List.map (fun (id, def) -> id, un_anf ident_info env def) defs
|
||||
in
|
||||
let body = un_anf ident_info env body in
|
||||
Uletrec (defs, body), Fixed
|
||||
| Uprim (prim, args, dbg) ->
|
||||
let args, args_moveable = un_anf_list_and_moveable ident_info env args in
|
||||
let moveable =
|
||||
both_moveable args_moveable (primitive_moveable prim args ident_info)
|
||||
in
|
||||
Uprim (prim, args, dbg), moveable
|
||||
| Uswitch (cond, sw) ->
|
||||
let cond = un_anf ident_info env cond in
|
||||
let sw =
|
||||
{ sw with
|
||||
us_actions_consts = un_anf_array ident_info env sw.us_actions_consts;
|
||||
us_actions_blocks = un_anf_array ident_info env sw.us_actions_blocks;
|
||||
}
|
||||
in
|
||||
Uswitch (cond, sw), Fixed
|
||||
| Ustringswitch (cond, branches, default) ->
|
||||
let cond = un_anf ident_info env cond in
|
||||
let branches =
|
||||
List.map (fun (s, branch) -> s, un_anf ident_info env branch)
|
||||
branches
|
||||
in
|
||||
let default = Misc.may_map (un_anf ident_info env) default in
|
||||
Ustringswitch (cond, branches, default), Fixed
|
||||
| Ustaticfail (n, args) ->
|
||||
let args = un_anf_list ident_info env args in
|
||||
Ustaticfail (n, args), Fixed
|
||||
| Ucatch (n, ids, body, handler) ->
|
||||
let body = un_anf ident_info env body in
|
||||
let handler = un_anf ident_info env handler in
|
||||
Ucatch (n, ids, body, handler), Fixed
|
||||
| Utrywith (body, id, handler) ->
|
||||
let body = un_anf ident_info env body in
|
||||
let handler = un_anf ident_info env handler in
|
||||
Utrywith (body, id, handler), Fixed
|
||||
| Uifthenelse (cond, ifso, ifnot) ->
|
||||
let cond, cond_moveable = un_anf_and_moveable ident_info env cond in
|
||||
let ifso, ifso_moveable = un_anf_and_moveable ident_info env ifso in
|
||||
let ifnot, ifnot_moveable = un_anf_and_moveable ident_info env ifnot in
|
||||
let moveable =
|
||||
both_moveable cond_moveable
|
||||
(both_moveable ifso_moveable ifnot_moveable)
|
||||
in
|
||||
Uifthenelse (cond, ifso, ifnot), moveable
|
||||
| Usequence (e1, e2) ->
|
||||
let e1 = un_anf ident_info env e1 in
|
||||
let e2 = un_anf ident_info env e2 in
|
||||
Usequence (e1, e2), Fixed
|
||||
| Uwhile (cond, body) ->
|
||||
let env = going_into_loop env in
|
||||
let cond = un_anf ident_info env cond in
|
||||
let body = un_anf ident_info env body in
|
||||
Uwhile (cond, body), Fixed
|
||||
| Ufor (id, low, high, direction, body) ->
|
||||
let low = un_anf ident_info env low in
|
||||
let high = un_anf ident_info env high in
|
||||
let body = un_anf ident_info (going_into_loop env) body in
|
||||
Ufor (id, low, high, direction, body), Fixed
|
||||
| Uassign (id, expr) ->
|
||||
let expr = un_anf ident_info env expr in
|
||||
Uassign (id, expr), Fixed
|
||||
| Usend (kind, e1, e2, args, dbg) ->
|
||||
let e1 = un_anf ident_info env e1 in
|
||||
let e2 = un_anf ident_info env e2 in
|
||||
let args = un_anf_list ident_info env args in
|
||||
Usend (kind, e1, e2, args, dbg), Fixed
|
||||
| Uunreachable ->
|
||||
Uunreachable, Fixed
|
||||
|
||||
and un_anf ident_info env clam : Clambda.ulambda =
|
||||
let clam, _moveable = un_anf_and_moveable ident_info env clam in
|
||||
clam
|
||||
|
||||
and un_anf_list_and_moveable ident_info env clams
|
||||
: Clambda.ulambda list * moveable =
|
||||
List.fold_right (fun clam (l, acc_moveable) ->
|
||||
let clam, moveable = un_anf_and_moveable ident_info env clam in
|
||||
clam :: l, both_moveable moveable acc_moveable)
|
||||
clams ([], (Moveable : moveable))
|
||||
|
||||
and un_anf_list ident_info env clams : Clambda.ulambda list =
|
||||
let clams, _moveable = un_anf_list_and_moveable ident_info env clams in
|
||||
clams
|
||||
|
||||
and un_anf_array ident_info env clams : Clambda.ulambda array =
|
||||
Array.map (un_anf ident_info env) clams
|
||||
|
||||
let apply clam ~what =
|
||||
if not Config.flambda then clam
|
||||
else begin
|
||||
let ident_info = make_ident_info clam in
|
||||
let let_bound_vars_that_can_be_moved =
|
||||
let_bound_vars_that_can_be_moved ident_info clam
|
||||
in
|
||||
let clam =
|
||||
substitute_let_moveable let_bound_vars_that_can_be_moved
|
||||
Ident.Map.empty clam
|
||||
in
|
||||
let ident_info = make_ident_info clam in
|
||||
let clam = un_anf ident_info Ident.Map.empty clam in
|
||||
if !Clflags.dump_clambda then begin
|
||||
Format.eprintf "@.un-anf (%s):@ %a@." what Printclambda.clambda clam
|
||||
end;
|
||||
clam
|
||||
end
|
|
@ -0,0 +1,22 @@
|
|||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Pierre Chambart, OCamlPro *)
|
||||
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
||||
(* *)
|
||||
(* Copyright 2013--2016 OCamlPro SAS *)
|
||||
(* Copyright 2014--2016 Jane Street Group LLC *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file ../LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(** Expand ANF-like constructs so that pattern matches in [Cmmgen] will
|
||||
work correctly. *)
|
||||
val apply
|
||||
: Clambda.ulambda
|
||||
-> what:string
|
||||
-> Clambda.ulambda
|
2478
asmrun/.depend
2478
asmrun/.depend
File diff suppressed because it is too large
Load Diff
|
@ -18,12 +18,13 @@ FLAGS=-I../byterun -DCAML_NAME_SPACE -DNATIVE_CODE \
|
|||
-DTARGET_$(ARCH) -DMODEL_$(MODEL) -DSYS_$(SYSTEM) $(IFLEXDIR)
|
||||
CFLAGS=$(FLAGS) $(NATIVECCCOMPOPTS)
|
||||
DFLAGS=$(FLAGS) -g -DDEBUG $(NATIVECCCOMPOPTS)
|
||||
IFLAGS=$(FLAGS) -DCAML_INSTR
|
||||
PFLAGS=$(FLAGS) -pg -DPROFILING $(NATIVECCPROFOPTS) $(NATIVECCCOMPOPTS)
|
||||
PICFLAGS=$(FLAGS) $(SHAREDCCCOMPOPTS) $(NATIVECCCOMPOPTS)
|
||||
|
||||
COBJS=startup_aux.o startup.o \
|
||||
main.o fail.o roots.o globroots.o signals.o signals_asm.o \
|
||||
misc.o freelist.o major_gc.o minor_gc.o memory.o alloc.o compare.o ints.o \
|
||||
freelist.o misc.o major_gc.o minor_gc.o memory.o alloc.o compare.o ints.o \
|
||||
floats.o str.o array.o io.o extern.o intern.o hash.o sys.o parsing.o \
|
||||
gc_ctrl.o terminfo.o md5.o obj.o lexing.o printexc.o callback.o weak.o \
|
||||
compact.o finalise.o custom.o $(UNIX_OR_WIN32).o backtrace_prim.o \
|
||||
|
@ -34,11 +35,16 @@ ASMOBJS=$(ARCH).o
|
|||
|
||||
OBJS=$(COBJS) $(ASMOBJS)
|
||||
DOBJS=$(COBJS:.o=.d.o) $(ASMOBJS)
|
||||
IOBJS=$(COBJS:.o=.i.o) $(ASMOBJS)
|
||||
POBJS=$(COBJS:.o=.p.o) $(ASMOBJS:.o=.p.o)
|
||||
PICOBJS=$(COBJS:.o=.pic.o) $(ASMOBJS:.o=.pic.o)
|
||||
|
||||
all: libasmrun.a all-$(RUNTIMED) all-$(PROFILING) all-$(SHARED)
|
||||
|
||||
ifeq "$(RUNTIMEI)" "true"
|
||||
all: libasmruni.a
|
||||
endif
|
||||
|
||||
libasmrun.a: $(OBJS)
|
||||
rm -f libasmrun.a
|
||||
$(ARCMD) rc libasmrun.a $(OBJS)
|
||||
|
@ -55,6 +61,11 @@ libasmrund.a: $(DOBJS)
|
|||
$(ARCMD) rc libasmrund.a $(DOBJS)
|
||||
$(RANLIB) libasmrund.a
|
||||
|
||||
libasmruni.a: $(IOBJS)
|
||||
rm -f $@
|
||||
$(ARCMD) rc $@ $^
|
||||
$(RANLIB) $@
|
||||
|
||||
all-noprof:
|
||||
|
||||
all-prof: libasmrunp.a
|
||||
|
@ -78,40 +89,39 @@ libasmrun_shared.so: $(PICOBJS)
|
|||
|
||||
INSTALL_LIBDIR=$(DESTDIR)$(LIBDIR)
|
||||
|
||||
install: install-default install-$(RUNTIMED) install-$(PROFILING) \
|
||||
install-$(SHARED)
|
||||
|
||||
install-default:
|
||||
install::
|
||||
cp libasmrun.a $(INSTALL_LIBDIR)/libasmrun.a
|
||||
cd $(INSTALL_LIBDIR); $(RANLIB) libasmrun.a
|
||||
.PHONY: install-default
|
||||
|
||||
install-noruntimed:
|
||||
.PHONY: install-noruntimed
|
||||
|
||||
install-runtimed:
|
||||
ifeq "$(RUNTIMED)" "runtimed"
|
||||
install::
|
||||
cp libasmrund.a $(INSTALL_LIBDIR)/libasmrund.a
|
||||
cd $(INSTALL_LIBDIR); $(RANLIB) libasmrund.a
|
||||
.PHONY: install-runtimed
|
||||
endif
|
||||
|
||||
install-noprof:
|
||||
rm -f $(INSTALL_LIBDIR)/libasmrunp.a
|
||||
ln -s libasmrun.a $(INSTALL_LIBDIR)/libasmrunp.a
|
||||
.PHONY: install-noprof
|
||||
ifeq "$(RUNTIMEI)" "true"
|
||||
install::
|
||||
cp libasmruni.a $(INSTALL_LIBDIR)/libasmruni.a
|
||||
cd $(INSTALL_LIBDIR); $(RANLIB) libasmruni.a
|
||||
endif
|
||||
|
||||
install-prof:
|
||||
ifeq "$(PROFILING)" "prof"
|
||||
install::
|
||||
cp libasmrunp.a $(INSTALL_LIBDIR)/libasmrunp.a
|
||||
cd $(INSTALL_LIBDIR); $(RANLIB) libasmrunp.a
|
||||
.PHONY: install-prof
|
||||
else
|
||||
install::
|
||||
rm -f $(INSTALL_LIBDIR)/libasmrunp.a
|
||||
ln -s libasmrun.a $(INSTALL_LIBDIR)/libasmrunp.a
|
||||
endif
|
||||
|
||||
install-noshared:
|
||||
.PHONY: install-noshared
|
||||
|
||||
install-shared:
|
||||
ifeq "$(SHARED)" "shared"
|
||||
install::
|
||||
cp libasmrun_pic.a $(INSTALL_LIBDIR)/libasmrun_pic.a
|
||||
cd $(INSTALL_LIBDIR); $(RANLIB) libasmrunp.a
|
||||
cp libasmrun_shared.so $(INSTALL_LIBDIR)/libasmrun_shared.so
|
||||
.PHONY: install-prof
|
||||
endif
|
||||
|
||||
main.c: ../byterun/main.c
|
||||
ln -s ../byterun/main.c main.c
|
||||
|
@ -197,6 +207,18 @@ LINKEDFILES=misc.c freelist.c major_gc.c minor_gc.c memory.c alloc.c array.c \
|
|||
clean::
|
||||
rm -f $(LINKEDFILES)
|
||||
|
||||
%.d.o: %.c
|
||||
$(CC) -c $(DFLAGS) -o $@ $<
|
||||
|
||||
%.i.o : %.c
|
||||
$(CC) -c $(IFLAGS) -o $@ $<
|
||||
|
||||
%.p.o: %.c
|
||||
$(CC) -c $(PFLAGS) -o $@ $<
|
||||
|
||||
%.pic.o: %.c
|
||||
$(CC) -c $(PICFLAGS) -o $@ $<
|
||||
|
||||
%.o: %.S
|
||||
$(ASPP) -DSYS_$(SYSTEM) -DMODEL_$(MODEL) -o $@ $< || \
|
||||
{ echo "If your assembler produced syntax errors, it is probably";\
|
||||
|
@ -210,15 +232,6 @@ clean::
|
|||
%.pic.o: %.S
|
||||
$(ASPP) -DSYS_$(SYSTEM) -DMODEL_$(MODEL) $(SHAREDCCCOMPOPTS) -o $@ $<
|
||||
|
||||
%.d.o: %.c
|
||||
$(CC) -c $(DFLAGS) -o $@ $<
|
||||
|
||||
%.p.o: %.c
|
||||
$(CC) -c $(PFLAGS) -o $@ $<
|
||||
|
||||
%.pic.o: %.c
|
||||
$(CC) -c $(PICFLAGS) -o $@ $<
|
||||
|
||||
%.o: %.s
|
||||
$(ASPP) -DSYS_$(SYSTEM) -o $@ $<
|
||||
|
||||
|
@ -233,7 +246,8 @@ clean::
|
|||
|
||||
depend: $(COBJS:.o=.c) ${LINKEDFILES}
|
||||
$(CC) -MM $(FLAGS) *.c > .depend
|
||||
$(CC) -MM $(FLAGS) -DPROFILING *.c | sed -e 's/\.o/.p.o/' >> .depend
|
||||
$(CC) -MM $(FLAGS) -DDEBUG *.c | sed -e 's/\.o/.d.o/' >> .depend
|
||||
$(CC) -MM $(FLAGS) *.c | sed -e 's/\.o/.p.o/' >> .depend
|
||||
$(CC) -MM $(FLAGS) -DCAML_INSTR *.c | sed -e 's/\.o/.i.o/' >> .depend
|
||||
|
||||
include .depend
|
||||
|
|
|
@ -518,3 +518,6 @@ caml_system__frametable:
|
|||
.align 2
|
||||
.type caml_system__frametable, %object
|
||||
.size caml_system__frametable, .-caml_system__frametable
|
||||
|
||||
/* Mark stack as non-executable */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
|
|
|
@ -553,3 +553,6 @@ caml_system__frametable:
|
|||
.align 3
|
||||
.type caml_system__frametable, %object
|
||||
.size caml_system__frametable, .-caml_system__frametable
|
||||
|
||||
/* Mark stack as non-executable */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/***********************************************************************/
|
||||
/* */
|
||||
/* OCaml */
|
||||
/* */
|
||||
/* Pierre Chambart, OCamlPro */
|
||||
/* Mark Shinwell, Jane Street Europe */
|
||||
/* */
|
||||
/* Copyright 2015 Institut National de Recherche en Informatique et */
|
||||
/* en Automatique. All rights reserved. This file is distributed */
|
||||
/* under the terms of the GNU Library General Public License, with */
|
||||
/* the special exception on linking described in file ../LICENSE. */
|
||||
/* */
|
||||
/***********************************************************************/
|
||||
|
||||
/* Runtime checks to try to catch errors in code generation.
|
||||
See flambda_to_clambda.ml for more information. */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <caml/mlvalues.h>
|
||||
|
||||
value caml_check_value_is_closure(value v, value v_descr)
|
||||
{
|
||||
const char* descr = String_val(v_descr);
|
||||
value orig_v = v;
|
||||
|
||||
if (v == (value) 0) {
|
||||
fprintf(stderr, "NULL is not a closure: %s\n",
|
||||
descr);
|
||||
abort();
|
||||
}
|
||||
if (!Is_block(v)) {
|
||||
fprintf(stderr,
|
||||
"Expecting a closure, got a non-boxed value %p: %s\n",
|
||||
(void*) v, descr);
|
||||
abort();
|
||||
}
|
||||
if (!(Tag_val(v) == Closure_tag || Tag_val(v) == Infix_tag)) {
|
||||
fprintf(stderr,
|
||||
"Expecting a closure, got a boxed value with tag %i: %s\n",
|
||||
Tag_val(v), descr);
|
||||
abort();
|
||||
}
|
||||
if (Tag_val(v) == Infix_tag) {
|
||||
v -= Infix_offset_val(v);
|
||||
assert(Tag_val(v) == Closure_tag);
|
||||
}
|
||||
assert(Wosize_val(v) >= 2);
|
||||
|
||||
return orig_v;
|
||||
}
|
||||
|
||||
value caml_check_field_access(value v, value pos, value v_descr)
|
||||
{
|
||||
const char* descr = String_val(v_descr);
|
||||
value orig_v = v;
|
||||
if (v == (value) 0) {
|
||||
fprintf(stderr, "Access to field %lld of NULL: %s\n",
|
||||
(unsigned long long) Long_val(pos), descr);
|
||||
abort();
|
||||
}
|
||||
if (!Is_block(v)) {
|
||||
fprintf(stderr,
|
||||
"Access to field %lld of non-boxed value %p is illegal: %s\n",
|
||||
(unsigned long long) Long_val(pos), (void*) v, descr);
|
||||
abort();
|
||||
}
|
||||
if (Tag_val(v) == Infix_tag) {
|
||||
uintnat offset = Infix_offset_val(v);
|
||||
v -= offset;
|
||||
pos += offset / sizeof(value);
|
||||
}
|
||||
assert(Long_val(pos) >= 0);
|
||||
if (Long_val(pos) >= Wosize_val(v)) {
|
||||
fprintf(stderr,
|
||||
"Access to field %lld of value %p of size %lld is illegal: %s\n",
|
||||
(unsigned long long) Long_val(pos), (void*) v,
|
||||
(unsigned long long) Wosize_val(v),
|
||||
descr);
|
||||
abort();
|
||||
}
|
||||
return orig_v;
|
||||
}
|
|
@ -690,3 +690,6 @@ TOCENTRY(caml_young_limit)
|
|||
TOCENTRY(caml_young_ptr)
|
||||
|
||||
#endif
|
||||
|
||||
/* Mark stack as non-executable */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
|
|
|
@ -335,27 +335,78 @@ void caml_oldify_local_roots (void)
|
|||
if (caml_scan_roots_hook != NULL) (*caml_scan_roots_hook)(&caml_oldify_one);
|
||||
}
|
||||
|
||||
/* Call [darken] on all roots */
|
||||
uintnat caml_incremental_roots_count = 0;
|
||||
|
||||
void caml_darken_all_roots (void)
|
||||
/* Call [caml_darken] on all roots, incrementally:
|
||||
[caml_darken_all_roots_start] does the non-incremental part and
|
||||
sets things up for [caml_darken_all_roots_slice].
|
||||
*/
|
||||
void caml_darken_all_roots_start (void)
|
||||
{
|
||||
caml_do_roots (caml_darken);
|
||||
caml_do_roots (caml_darken, 0);
|
||||
}
|
||||
|
||||
void caml_do_roots (scanning_action f)
|
||||
/* Call [caml_darken] on at most [work] global roots. Return the
|
||||
amount of work not done, if any. If this is strictly positive,
|
||||
the darkening is done.
|
||||
*/
|
||||
intnat caml_darken_all_roots_slice (intnat work)
|
||||
{
|
||||
static int i, j;
|
||||
static value *glob;
|
||||
static int do_resume = 0;
|
||||
static mlsize_t roots_count = 0;
|
||||
intnat remaining_work = work;
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
|
||||
/* If the loop was started in a previous call, resume it. */
|
||||
if (do_resume) goto resume;
|
||||
|
||||
/* This is the same loop as in [caml_do_roots], but we make it
|
||||
suspend itself when [work] reaches 0. */
|
||||
for (i = 0; caml_globals[i] != 0; i++) {
|
||||
for(glob = caml_globals[i]; *glob != 0; glob++) {
|
||||
for (j = 0; j < Wosize_val(*glob); j++){
|
||||
caml_darken (Field (*glob, j), &Field (*glob, j));
|
||||
-- remaining_work;
|
||||
if (remaining_work == 0){
|
||||
roots_count += work;
|
||||
do_resume = 1;
|
||||
goto suspend;
|
||||
}
|
||||
resume: ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The loop finished normally, so all roots are now darkened. */
|
||||
caml_incremental_roots_count = roots_count + work - remaining_work;
|
||||
/* Prepare for the next run. */
|
||||
do_resume = 0;
|
||||
roots_count = 0;
|
||||
|
||||
suspend:
|
||||
/* Do this in both cases. */
|
||||
CAML_INSTR_TIME (tmr, "major/mark/global_roots_slice");
|
||||
return remaining_work;
|
||||
}
|
||||
|
||||
void caml_do_roots (scanning_action f, int do_globals)
|
||||
{
|
||||
int i, j;
|
||||
value * glob;
|
||||
link *lnk;
|
||||
CAML_INSTR_SETUP (tmr, "major_roots");
|
||||
|
||||
/* The global roots */
|
||||
for (i = 0; caml_globals[i] != 0; i++) {
|
||||
for(glob = caml_globals[i]; *glob != 0; glob++) {
|
||||
for (j = 0; j < Wosize_val(*glob); j++)
|
||||
f (Field (*glob, j), &Field (*glob, j));
|
||||
if (do_globals){
|
||||
/* The global roots */
|
||||
for (i = 0; caml_globals[i] != 0; i++) {
|
||||
for(glob = caml_globals[i]; *glob != 0; glob++) {
|
||||
for (j = 0; j < Wosize_val(*glob); j++)
|
||||
f (Field (*glob, j), &Field (*glob, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dynamic global roots */
|
||||
iter_list(caml_dyn_globals, lnk) {
|
||||
for(glob = (value *) lnk->data; *glob != 0; glob++) {
|
||||
|
@ -364,16 +415,20 @@ void caml_do_roots (scanning_action f)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAML_INSTR_TIME (tmr, "major_roots/dynamic_global");
|
||||
/* The stack and local roots */
|
||||
caml_do_local_roots(f, caml_bottom_of_stack, caml_last_return_address,
|
||||
caml_gc_regs, caml_local_roots);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/local");
|
||||
/* Global C roots */
|
||||
caml_scan_global_roots(f);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/C");
|
||||
/* Finalised values */
|
||||
caml_final_do_strong_roots (f);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/finalised");
|
||||
/* Hook */
|
||||
if (caml_scan_roots_hook != NULL) (*caml_scan_roots_hook)(f);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/hook");
|
||||
}
|
||||
|
||||
void caml_do_local_roots(scanning_action f, char * bottom_of_stack,
|
||||
|
|
|
@ -351,3 +351,6 @@ caml_system__frametable:
|
|||
.short -1 /* negative size count => use callback link */
|
||||
.short 0 /* no roots here */
|
||||
.align 8
|
||||
|
||||
/* Mark stack as non-executable */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
|
|
|
@ -67,9 +67,11 @@ extern char caml_system__code_begin, caml_system__code_end;
|
|||
|
||||
void caml_garbage_collection(void)
|
||||
{
|
||||
caml_young_limit = caml_young_start;
|
||||
if (caml_young_ptr < caml_young_start || caml_force_major_slice) {
|
||||
caml_minor_collection();
|
||||
CAMLassert (caml_young_ptr >= caml_young_alloc_start);
|
||||
caml_young_limit = caml_young_trigger;
|
||||
if (caml_requested_major_slice || caml_requested_minor_gc ||
|
||||
caml_young_ptr - caml_young_trigger < Max_young_whsize){
|
||||
caml_gc_dispatch ();
|
||||
}
|
||||
caml_process_pending_signals();
|
||||
}
|
||||
|
|
|
@ -107,14 +107,17 @@ void caml_main(char **argv)
|
|||
caml_install_invalid_parameter_handler();
|
||||
#endif
|
||||
caml_init_custom_operations();
|
||||
#ifdef DEBUG
|
||||
caml_verb_gc = 63;
|
||||
#endif
|
||||
caml_top_of_stack = &tos;
|
||||
#ifdef DEBUG
|
||||
caml_verb_gc = 0x3F;
|
||||
#endif
|
||||
caml_parse_ocamlrunparam();
|
||||
#ifdef DEBUG
|
||||
caml_gc_message (-1, "### OCaml runtime: debug mode ###\n", 0);
|
||||
#endif
|
||||
caml_init_gc (caml_init_minor_heap_wsz, caml_init_heap_wsz,
|
||||
caml_init_heap_chunk_sz, caml_init_percent_free,
|
||||
caml_init_max_percent_free);
|
||||
caml_init_max_percent_free, caml_init_major_window);
|
||||
init_static();
|
||||
caml_init_signals();
|
||||
caml_init_backtrace();
|
||||
|
|
BIN
boot/ocamlc
BIN
boot/ocamlc
Binary file not shown.
BIN
boot/ocamldep
BIN
boot/ocamldep
Binary file not shown.
BIN
boot/ocamllex
BIN
boot/ocamllex
Binary file not shown.
|
@ -184,6 +184,7 @@ type shared_code = (int * int) list
|
|||
|
||||
type function_attribute = {
|
||||
inline : inline_attribute;
|
||||
is_a_functor: bool;
|
||||
}
|
||||
|
||||
type lambda =
|
||||
|
@ -245,6 +246,7 @@ let lambda_unit = Lconst const_unit
|
|||
|
||||
let default_function_attribute = {
|
||||
inline = Default_inline;
|
||||
is_a_functor = false;
|
||||
}
|
||||
|
||||
(* Build sharing keys *)
|
||||
|
|
|
@ -196,6 +196,7 @@ type shared_code = (int * int) list (* stack size -> code label *)
|
|||
|
||||
type function_attribute = {
|
||||
inline : inline_attribute;
|
||||
is_a_functor: bool;
|
||||
}
|
||||
|
||||
type lambda =
|
||||
|
|
|
@ -261,7 +261,103 @@ let primitive ppf = function
|
|||
| Pbbswap(bi) -> print_boxed_integer "bswap" ppf bi
|
||||
| Pint_as_pointer -> fprintf ppf "int_as_pointer"
|
||||
|
||||
let function_attribute ppf { inline } =
|
||||
let name_of_primitive = function
|
||||
| Pidentity -> "Pidentity"
|
||||
| Pignore -> "Pignore"
|
||||
| Prevapply _ -> "Prevapply"
|
||||
| Pdirapply _ -> "Pdirapply"
|
||||
| Ploc _ -> "Ploc"
|
||||
| Pgetglobal _ -> "Pgetglobal"
|
||||
| Psetglobal _ -> "Psetglobal"
|
||||
| Pmakeblock _ -> "Pmakeblock"
|
||||
| Pfield _ -> "Pfield"
|
||||
| Psetfield _ -> "Psetfield"
|
||||
| Pfloatfield _ -> "Pfloatfield"
|
||||
| Psetfloatfield _ -> "Psetfloatfield"
|
||||
| Pduprecord _ -> "Pduprecord"
|
||||
| Plazyforce -> "Plazyforce"
|
||||
| Pccall _ -> "Pccall"
|
||||
| Praise _ -> "Praise"
|
||||
| Psequand -> "Psequand"
|
||||
| Psequor -> "Psequor"
|
||||
| Pnot -> "Pnot"
|
||||
| Pnegint -> "Pnegint"
|
||||
| Paddint -> "Paddint"
|
||||
| Psubint -> "Psubint"
|
||||
| Pmulint -> "Pmulint"
|
||||
| Pdivint -> "Pdivint"
|
||||
| Pmodint -> "Pmodint"
|
||||
| Pandint -> "Pandint"
|
||||
| Porint -> "Porint"
|
||||
| Pxorint -> "Pxorint"
|
||||
| Plslint -> "Plslint"
|
||||
| Plsrint -> "Plsrint"
|
||||
| Pasrint -> "Pasrint"
|
||||
| Pintcomp _ -> "Pintcomp"
|
||||
| Poffsetint _ -> "Poffsetint"
|
||||
| Poffsetref _ -> "Poffsetref"
|
||||
| Pintoffloat -> "Pintoffloat"
|
||||
| Pfloatofint -> "Pfloatofint"
|
||||
| Pnegfloat -> "Pnegfloat"
|
||||
| Pabsfloat -> "Pabsfloat"
|
||||
| Paddfloat -> "Paddfloat"
|
||||
| Psubfloat -> "Psubfloat"
|
||||
| Pmulfloat -> "Pmulfloat"
|
||||
| Pdivfloat -> "Pdivfloat"
|
||||
| Pfloatcomp _ -> "Pfloatcomp"
|
||||
| Pstringlength -> "Pstringlength"
|
||||
| Pstringrefu -> "Pstringrefu"
|
||||
| Pstringsetu -> "Pstringsetu"
|
||||
| Pstringrefs -> "Pstringrefs"
|
||||
| Pstringsets -> "Pstringsets"
|
||||
| Parraylength _ -> "Parraylength"
|
||||
| Pmakearray _ -> "Pmakearray"
|
||||
| Parrayrefu _ -> "Parrayrefu"
|
||||
| Parraysetu _ -> "Parraysetu"
|
||||
| Parrayrefs _ -> "Parrayrefs"
|
||||
| Parraysets _ -> "Parraysets"
|
||||
| Pctconst _ -> "Pctconst"
|
||||
| Pisint -> "Pisint"
|
||||
| Pisout -> "Pisout"
|
||||
| Pbittest -> "Pbittest"
|
||||
| Pbintofint _ -> "Pbintofint"
|
||||
| Pintofbint _ -> "Pintofbint"
|
||||
| Pcvtbint _ -> "Pcvtbint"
|
||||
| Pnegbint _ -> "Pnegbint"
|
||||
| Paddbint _ -> "Paddbint"
|
||||
| Psubbint _ -> "Psubbint"
|
||||
| Pmulbint _ -> "Pmulbint"
|
||||
| Pdivbint _ -> "Pdivbint"
|
||||
| Pmodbint _ -> "Pmodbint"
|
||||
| Pandbint _ -> "Pandbint"
|
||||
| Porbint _ -> "Porbint"
|
||||
| Pxorbint _ -> "Pxorbint"
|
||||
| Plslbint _ -> "Plslbint"
|
||||
| Plsrbint _ -> "Plsrbint"
|
||||
| Pasrbint _ -> "Pasrbint"
|
||||
| Pbintcomp _ -> "Pbintcomp"
|
||||
| Pbigarrayref _ -> "Pbigarrayref"
|
||||
| Pbigarrayset _ -> "Pbigarrayset"
|
||||
| Pbigarraydim _ -> "Pbigarraydim"
|
||||
| Pstring_load_16 _ -> "Pstring_load_16"
|
||||
| Pstring_load_32 _ -> "Pstring_load_32"
|
||||
| Pstring_load_64 _ -> "Pstring_load_64"
|
||||
| Pstring_set_16 _ -> "Pstring_set_16"
|
||||
| Pstring_set_32 _ -> "Pstring_set_32"
|
||||
| Pstring_set_64 _ -> "Pstring_set_64"
|
||||
| Pbigstring_load_16 _ -> "Pbigstring_load_16"
|
||||
| Pbigstring_load_32 _ -> "Pbigstring_load_32"
|
||||
| Pbigstring_load_64 _ -> "Pbigstring_load_64"
|
||||
| Pbigstring_set_16 _ -> "Pbigstring_set_16"
|
||||
| Pbigstring_set_32 _ -> "Pbigstring_set_32"
|
||||
| Pbigstring_set_64 _ -> "Pbigstring_set_64"
|
||||
| Pbswap16 -> "Pbswap16"
|
||||
| Pbbswap _ -> "Pbbswap"
|
||||
| Pint_as_pointer -> "Pint_as_pointer"
|
||||
|
||||
let function_attribute ppf { inline; is_a_functor } =
|
||||
if is_a_functor then
|
||||
fprintf ppf "is_a_functor@ ";
|
||||
match inline with
|
||||
| Default_inline -> ()
|
||||
| Always_inline -> fprintf ppf "always_inline@ "
|
||||
|
|
|
@ -17,3 +17,4 @@ open Format
|
|||
val structured_constant: formatter -> structured_constant -> unit
|
||||
val lambda: formatter -> lambda -> unit
|
||||
val primitive: formatter -> primitive -> unit
|
||||
val name_of_primitive : primitive -> string
|
||||
|
|
|
@ -73,7 +73,7 @@ let add_inline_attribute expr loc attributes =
|
|||
Location.prerr_warning loc
|
||||
(Warnings.Duplicated_attribute "inline")
|
||||
end;
|
||||
Lfunction { funct with attr = { inline = inline_attribute } }
|
||||
Lfunction { funct with attr = { attr with inline = inline_attribute } }
|
||||
| expr, (Always_inline | Never_inline) ->
|
||||
Location.prerr_warning loc
|
||||
(Warnings.Misplaced_attribute "inline");
|
||||
|
@ -84,12 +84,18 @@ let add_inline_attribute expr loc attributes =
|
|||
used to ensure that this expression is not misplaced: If it
|
||||
appears on any expression, it is an error, otherwise it would
|
||||
have been removed by this function *)
|
||||
let get_inlined_attribute e =
|
||||
let get_and_remove_inlined_attribute e =
|
||||
let attribute_value, exp_attributes =
|
||||
make_get_inline_attribute is_inlined_attribute e.exp_attributes
|
||||
in
|
||||
attribute_value, { e with exp_attributes }
|
||||
|
||||
let get_and_remove_inlined_attribute_on_module e =
|
||||
let attribute_value, mod_attributes =
|
||||
make_get_inline_attribute is_inlined_attribute e.mod_attributes
|
||||
in
|
||||
attribute_value, { e with mod_attributes }
|
||||
|
||||
(* It also remove the attribute from the expression, like
|
||||
get_inlined_attribute *)
|
||||
let get_tailcall_attribute e =
|
||||
|
@ -125,3 +131,18 @@ let check_attribute e ({ txt; loc }, _) =
|
|||
Location.prerr_warning loc
|
||||
(Warnings.Misplaced_attribute txt)
|
||||
| _ -> ()
|
||||
|
||||
let check_attribute_on_module e ({ txt; loc }, _) =
|
||||
match txt with
|
||||
| "inline" | "ocaml.inline" -> begin
|
||||
match e.mod_desc with
|
||||
| Tmod_functor _ -> ()
|
||||
| _ ->
|
||||
Location.prerr_warning loc
|
||||
(Warnings.Misplaced_attribute txt)
|
||||
end
|
||||
| "inlined" | "ocaml.inlined" ->
|
||||
(* Removed by the Texp_apply cases *)
|
||||
Location.prerr_warning loc
|
||||
(Warnings.Misplaced_attribute txt)
|
||||
| _ -> ()
|
||||
|
|
|
@ -15,20 +15,29 @@ val check_attribute
|
|||
-> string Location.loc * _
|
||||
-> unit
|
||||
|
||||
val check_attribute_on_module
|
||||
: Typedtree.module_expr
|
||||
-> string Location.loc * _
|
||||
-> unit
|
||||
|
||||
val add_inline_attribute
|
||||
: Lambda.lambda
|
||||
-> Location.t
|
||||
-> Parsetree.attribute list
|
||||
-> Parsetree.attributes
|
||||
-> Lambda.lambda
|
||||
|
||||
val get_inline_attribute
|
||||
: (string Location.loc * Parsetree.payload) list
|
||||
: Parsetree.attributes
|
||||
-> Lambda.inline_attribute
|
||||
|
||||
val get_inlined_attribute
|
||||
val get_and_remove_inlined_attribute
|
||||
: Typedtree.expression
|
||||
-> Lambda.inline_attribute * Typedtree.expression
|
||||
|
||||
val get_and_remove_inlined_attribute_on_module
|
||||
: Typedtree.module_expr
|
||||
-> Lambda.inline_attribute * Typedtree.module_expr
|
||||
|
||||
val get_tailcall_attribute
|
||||
: Typedtree.expression
|
||||
-> bool * Typedtree.expression
|
||||
|
|
|
@ -692,6 +692,7 @@ and transl_exp0 e =
|
|||
transl_function e.exp_loc !Clflags.native_code repr partial pl)
|
||||
in
|
||||
let attr = {
|
||||
default_function_attribute with
|
||||
inline = Translattribute.get_inline_attribute e.exp_attributes;
|
||||
}
|
||||
in
|
||||
|
@ -709,7 +710,7 @@ and transl_exp0 e =
|
|||
Translattribute.get_tailcall_attribute funct
|
||||
in
|
||||
let inlined, funct =
|
||||
Translattribute.get_inlined_attribute funct
|
||||
Translattribute.get_and_remove_inlined_attribute funct
|
||||
in
|
||||
let e = { e with exp_desc = Texp_apply(funct, oargs) } in
|
||||
event_after e (transl_apply ~should_be_tailcall ~inlined
|
||||
|
@ -765,7 +766,7 @@ and transl_exp0 e =
|
|||
Translattribute.get_tailcall_attribute funct
|
||||
in
|
||||
let inlined, funct =
|
||||
Translattribute.get_inlined_attribute funct
|
||||
Translattribute.get_and_remove_inlined_attribute funct
|
||||
in
|
||||
let e = { e with exp_desc = Texp_apply(funct, oargs) } in
|
||||
event_after e (transl_apply ~should_be_tailcall ~inlined
|
||||
|
|
|
@ -91,7 +91,8 @@ let rec apply_coercion strict restr arg =
|
|||
let param = Ident.create "funarg" in
|
||||
name_lambda strict arg (fun id ->
|
||||
Lfunction{kind = Curried; params = [param];
|
||||
attr = default_function_attribute;
|
||||
attr = { default_function_attribute with
|
||||
is_a_functor = true };
|
||||
body = apply_coercion
|
||||
Strict cc_res
|
||||
(Lapply{ap_should_be_tailcall=false;
|
||||
|
@ -348,6 +349,8 @@ let transl_class_bindings cl_list =
|
|||
(* Compile a module expression *)
|
||||
|
||||
let rec transl_module cc rootpath mexp =
|
||||
List.iter (Translattribute.check_attribute_on_module mexp)
|
||||
mexp.mod_attributes;
|
||||
match mexp.mod_type with
|
||||
Mty_alias _ -> apply_coercion Alias cc lambda_unit
|
||||
| _ ->
|
||||
|
@ -356,19 +359,24 @@ let rec transl_module cc rootpath mexp =
|
|||
apply_coercion Strict cc
|
||||
(transl_path ~loc:mexp.mod_loc mexp.mod_env path)
|
||||
| Tmod_structure str ->
|
||||
transl_struct [] cc rootpath str
|
||||
fst (transl_struct [] cc rootpath str)
|
||||
| Tmod_functor( param, _, mty, body) ->
|
||||
let bodypath = functor_path rootpath param in
|
||||
let inline_attribute =
|
||||
Translattribute.get_inline_attribute mexp.mod_attributes
|
||||
in
|
||||
oo_wrap mexp.mod_env true
|
||||
(function
|
||||
| Tcoerce_none ->
|
||||
Lfunction{kind = Curried; params = [param];
|
||||
attr = default_function_attribute;
|
||||
attr = { inline = inline_attribute;
|
||||
is_a_functor = true };
|
||||
body = transl_module Tcoerce_none bodypath body}
|
||||
| Tcoerce_functor(ccarg, ccres) ->
|
||||
let param' = Ident.create "funarg" in
|
||||
Lfunction{kind = Curried; params = [param'];
|
||||
attr = default_function_attribute;
|
||||
attr = { inline = inline_attribute;
|
||||
is_a_functor = true };
|
||||
body = Llet(Alias, param,
|
||||
apply_coercion Alias ccarg (Lvar param'),
|
||||
transl_module ccres bodypath body)}
|
||||
|
@ -376,13 +384,16 @@ let rec transl_module cc rootpath mexp =
|
|||
fatal_error "Translmod.transl_module")
|
||||
cc
|
||||
| Tmod_apply(funct, arg, ccarg) ->
|
||||
let inlined_attribute, funct =
|
||||
Translattribute.get_and_remove_inlined_attribute_on_module funct
|
||||
in
|
||||
oo_wrap mexp.mod_env true
|
||||
(apply_coercion Strict cc)
|
||||
(Lapply{ap_should_be_tailcall=false;
|
||||
ap_loc=mexp.mod_loc;
|
||||
ap_func=transl_module Tcoerce_none None funct;
|
||||
ap_args=[transl_module ccarg None arg];
|
||||
ap_inlined=Default_inline})
|
||||
ap_inlined=inlined_attribute})
|
||||
| Tmod_constraint(arg, mty, _, ccarg) ->
|
||||
transl_module (compose_coercions cc ccarg) rootpath arg
|
||||
| Tmod_unpack(arg, _) ->
|
||||
|
@ -396,7 +407,8 @@ and transl_structure fields cc rootpath = function
|
|||
begin match cc with
|
||||
Tcoerce_none ->
|
||||
Lprim(Pmakeblock(0, Immutable),
|
||||
List.map (fun id -> Lvar id) (List.rev fields))
|
||||
List.map (fun id -> Lvar id) (List.rev fields)),
|
||||
List.length fields
|
||||
| Tcoerce_structure(pos_cc_list, id_pos_list) ->
|
||||
(* Do not ignore id_pos_list ! *)
|
||||
(*Format.eprintf "%a@.@[" Includemod.print_coercion cc;
|
||||
|
@ -419,18 +431,20 @@ and transl_structure fields cc rootpath = function
|
|||
and id_pos_list =
|
||||
List.filter (fun (id,_,_) -> not (IdentSet.mem id ids)) id_pos_list
|
||||
in
|
||||
wrap_id_pos_list id_pos_list get_field lam
|
||||
wrap_id_pos_list id_pos_list get_field lam,
|
||||
List.length pos_cc_list
|
||||
| _ ->
|
||||
fatal_error "Translmod.transl_structure"
|
||||
end
|
||||
| item :: rem ->
|
||||
match item.str_desc with
|
||||
| Tstr_eval (expr, _) ->
|
||||
Lsequence(transl_exp expr, transl_structure fields cc rootpath rem)
|
||||
let body, size = transl_structure fields cc rootpath rem in
|
||||
Lsequence(transl_exp expr, body), size
|
||||
| Tstr_value(rec_flag, pat_expr_list) ->
|
||||
let ext_fields = rev_let_bound_idents pat_expr_list @ fields in
|
||||
transl_let rec_flag pat_expr_list
|
||||
(transl_structure ext_fields cc rootpath rem)
|
||||
let body, size = transl_structure ext_fields cc rootpath rem in
|
||||
transl_let rec_flag pat_expr_list body, size
|
||||
| Tstr_primitive descr ->
|
||||
record_primitive descr.val_val;
|
||||
transl_structure fields cc rootpath rem
|
||||
|
@ -438,31 +452,47 @@ and transl_structure fields cc rootpath = function
|
|||
transl_structure fields cc rootpath rem
|
||||
| Tstr_typext(tyext) ->
|
||||
let ids = List.map (fun ext -> ext.ext_id) tyext.tyext_constructors in
|
||||
transl_type_extension item.str_env rootpath tyext
|
||||
(transl_structure (List.rev_append ids fields) cc rootpath rem)
|
||||
let body, size =
|
||||
transl_structure (List.rev_append ids fields) cc rootpath rem
|
||||
in
|
||||
transl_type_extension item.str_env rootpath tyext body, size
|
||||
| Tstr_exception ext ->
|
||||
let id = ext.ext_id in
|
||||
let path = field_path rootpath id in
|
||||
let body, size = transl_structure (id :: fields) cc rootpath rem in
|
||||
Llet(Strict, id, transl_extension_constructor item.str_env path ext,
|
||||
transl_structure (id :: fields) cc rootpath rem)
|
||||
body), size
|
||||
| Tstr_module mb ->
|
||||
let id = mb.mb_id in
|
||||
let body, size = transl_structure (id :: fields) cc rootpath rem in
|
||||
let module_body =
|
||||
transl_module Tcoerce_none (field_path rootpath id) mb.mb_expr
|
||||
in
|
||||
let module_body =
|
||||
Translattribute.add_inline_attribute module_body mb.mb_loc mb.mb_attributes
|
||||
in
|
||||
Llet(pure_module mb.mb_expr, id,
|
||||
transl_module Tcoerce_none (field_path rootpath id) mb.mb_expr,
|
||||
transl_structure (id :: fields) cc rootpath rem)
|
||||
module_body,
|
||||
body), size
|
||||
| Tstr_recmodule bindings ->
|
||||
let ext_fields =
|
||||
List.rev_append (List.map (fun mb -> mb.mb_id) bindings) fields
|
||||
in
|
||||
compile_recmodule
|
||||
(fun id modl ->
|
||||
transl_module Tcoerce_none (field_path rootpath id) modl)
|
||||
bindings
|
||||
(transl_structure ext_fields cc rootpath rem)
|
||||
let body, size = transl_structure ext_fields cc rootpath rem in
|
||||
let lam =
|
||||
compile_recmodule
|
||||
(fun id modl ->
|
||||
transl_module Tcoerce_none (field_path rootpath id) modl)
|
||||
bindings
|
||||
body
|
||||
in
|
||||
lam, size
|
||||
| Tstr_class cl_list ->
|
||||
let (ids, class_bindings) = transl_class_bindings cl_list in
|
||||
Lletrec(class_bindings,
|
||||
transl_structure (List.rev_append ids fields) cc rootpath rem)
|
||||
let body, size =
|
||||
transl_structure (List.rev_append ids fields) cc rootpath rem
|
||||
in
|
||||
Lletrec(class_bindings, body), size
|
||||
| Tstr_include incl ->
|
||||
let ids = bound_value_identifiers incl.incl_type in
|
||||
let modl = incl.incl_mod in
|
||||
|
@ -471,10 +501,12 @@ and transl_structure fields cc rootpath = function
|
|||
[] ->
|
||||
transl_structure newfields cc rootpath rem
|
||||
| id :: ids ->
|
||||
Llet(Alias, id, Lprim(Pfield pos, [Lvar mid]),
|
||||
rebind_idents (pos + 1) (id :: newfields) ids) in
|
||||
let body, size = rebind_idents (pos + 1) (id :: newfields) ids in
|
||||
Llet(Alias, id, Lprim(Pfield pos, [Lvar mid]), body), size
|
||||
in
|
||||
let body, size = rebind_idents 0 fields ids in
|
||||
Llet(pure_module modl, mid, transl_module Tcoerce_none None modl,
|
||||
rebind_idents 0 fields ids)
|
||||
body), size
|
||||
|
||||
| Tstr_modtype _
|
||||
| Tstr_open _
|
||||
|
@ -526,16 +558,22 @@ let wrap_globals body =
|
|||
|
||||
(* Compile an implementation *)
|
||||
|
||||
let transl_implementation module_name (str, cc) =
|
||||
let transl_implementation_flambda module_name (str, cc) =
|
||||
reset_labels ();
|
||||
primitive_declarations := [];
|
||||
Hashtbl.clear used_primitives;
|
||||
let module_id = Ident.create_persistent module_name in
|
||||
let body =
|
||||
let body, size =
|
||||
transl_label_init
|
||||
(transl_struct [] cc (global_path module_id) str) in
|
||||
Lprim(Psetglobal module_id, [wrap_globals body])
|
||||
(fun () -> transl_struct [] cc (global_path module_id) str)
|
||||
in
|
||||
module_id, (wrap_globals body, size)
|
||||
|
||||
let transl_implementation module_name (str, cc) =
|
||||
let module_id, (module_initializer, _size) =
|
||||
transl_implementation_flambda module_name (str, cc)
|
||||
in
|
||||
Lprim (Psetglobal module_id, [module_initializer])
|
||||
|
||||
(* Build the list of value identifiers defined by a toplevel structure
|
||||
(excluding primitive declarations). *)
|
||||
|
@ -671,7 +709,11 @@ let transl_store_structure glob map prims str =
|
|||
let lam = transl_extension_constructor item.str_env path ext in
|
||||
Lsequence(Llet(Strict, id, subst_lambda subst lam, store_ident id),
|
||||
transl_store rootpath (add_ident false id subst) rem)
|
||||
| Tstr_module{mb_id=id; mb_expr={mod_desc = Tmod_structure str}} ->
|
||||
| Tstr_module{mb_id=id;
|
||||
mb_expr={mod_desc = Tmod_structure str} as mexp;
|
||||
mb_attributes} ->
|
||||
List.iter (Translattribute.check_attribute_on_module mexp)
|
||||
mb_attributes;
|
||||
let lam = transl_store (field_path rootpath id) subst str.str_items in
|
||||
(* Careful: see next case *)
|
||||
let subst = !transl_store_subst in
|
||||
|
@ -684,8 +726,17 @@ let transl_store_structure glob map prims str =
|
|||
Lsequence(store_ident id,
|
||||
transl_store rootpath (add_ident true id subst)
|
||||
rem)))
|
||||
| Tstr_module{mb_id=id; mb_expr={mod_desc = Tmod_constraint ({mod_desc = Tmod_structure str}, _, _, (Tcoerce_structure (map, _) as _cc))}} ->
|
||||
| Tstr_module{
|
||||
mb_id=id;
|
||||
mb_expr= {
|
||||
mod_desc = Tmod_constraint (
|
||||
{mod_desc = Tmod_structure str} as mexp, _, _,
|
||||
(Tcoerce_structure (map, _) as _cc))};
|
||||
mb_attributes
|
||||
} ->
|
||||
(* Format.printf "coerc id %s: %a@." (Ident.unique_name id) Includemod.print_coercion cc; *)
|
||||
List.iter (Translattribute.check_attribute_on_module mexp)
|
||||
mb_attributes;
|
||||
let lam = transl_store (field_path rootpath id) subst str.str_items in
|
||||
(* Careful: see next case *)
|
||||
let subst = !transl_store_subst in
|
||||
|
@ -704,8 +755,12 @@ let transl_store_structure glob map prims str =
|
|||
Lsequence(store_ident id,
|
||||
transl_store rootpath (add_ident true id subst)
|
||||
rem)))
|
||||
| Tstr_module{mb_id=id; mb_expr=modl} ->
|
||||
let lam = transl_module Tcoerce_none (field_path rootpath id) modl in
|
||||
| Tstr_module{mb_id=id; mb_expr=modl; mb_loc; mb_attributes} ->
|
||||
let lam =
|
||||
Translattribute.add_inline_attribute
|
||||
(transl_module Tcoerce_none (field_path rootpath id) modl)
|
||||
mb_loc mb_attributes
|
||||
in
|
||||
(* Careful: the module value stored in the global may be different
|
||||
from the local module value, in case a coercion is applied.
|
||||
If so, keep using the local module value (id) in the remainder of
|
||||
|
@ -884,7 +939,7 @@ let toploop_setvalue id lam =
|
|||
|
||||
let toploop_setvalue_id id = toploop_setvalue id (Lvar id)
|
||||
|
||||
let close_toplevel_term lam =
|
||||
let close_toplevel_term (lam, ()) =
|
||||
IdentSet.fold (fun id l -> Llet(Strict, id, toploop_getvalue id, l))
|
||||
(free_variables lam) lam
|
||||
|
||||
|
@ -953,7 +1008,8 @@ let transl_toplevel_item item =
|
|||
lambda_unit
|
||||
|
||||
let transl_toplevel_item_and_close itm =
|
||||
close_toplevel_term (transl_label_init (transl_toplevel_item itm))
|
||||
close_toplevel_term
|
||||
(transl_label_init (fun () -> transl_toplevel_item itm, ()))
|
||||
|
||||
let transl_toplevel_definition str =
|
||||
reset_labels ();
|
||||
|
|
|
@ -91,7 +91,8 @@ let prim_makearray =
|
|||
Primitive.simple ~name:"caml_make_vect" ~arity:2 ~alloc:true
|
||||
|
||||
(* Also use it for required globals *)
|
||||
let transl_label_init expr =
|
||||
let transl_label_init_general f =
|
||||
let expr, size = f () in
|
||||
let expr =
|
||||
Hashtbl.fold
|
||||
(fun c id expr -> Llet(Alias, id, Lconst c, expr))
|
||||
|
@ -104,7 +105,7 @@ let transl_label_init expr =
|
|||
in
|
||||
Env.reset_required_globals ();*)
|
||||
reset_labels ();
|
||||
expr
|
||||
expr, size
|
||||
|
||||
let transl_store_label_init glob size f arg =
|
||||
method_cache := Lprim(Pfield size, [Lprim(Pgetglobal glob, [])]);
|
||||
|
@ -118,7 +119,11 @@ let transl_store_label_init glob size f arg =
|
|||
Lprim (Pccall prim_makearray, [int !method_count; int 0])]),
|
||||
expr))
|
||||
in
|
||||
(size, transl_label_init expr)
|
||||
let lam, size = transl_label_init_general (fun () -> (expr, size)) in
|
||||
size, lam
|
||||
|
||||
let transl_label_init f =
|
||||
transl_label_init_general f
|
||||
|
||||
(* Share classes *)
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ val share: structured_constant -> lambda
|
|||
val meth: lambda -> string -> lambda * lambda list
|
||||
|
||||
val reset_labels: unit -> unit
|
||||
val transl_label_init: lambda -> lambda
|
||||
val transl_label_init: (unit -> lambda * 'a) -> lambda * 'a
|
||||
val transl_store_label_init:
|
||||
Ident.t -> int -> ('a -> lambda) -> 'a -> int * lambda
|
||||
|
||||
|
|
1514
byterun/.depend
1514
byterun/.depend
File diff suppressed because it is too large
Load Diff
|
@ -15,9 +15,11 @@ include Makefile.common
|
|||
|
||||
CFLAGS=-DCAML_NAME_SPACE $(BYTECCCOMPOPTS) $(IFLEXDIR)
|
||||
DFLAGS=-DCAML_NAME_SPACE -g -DDEBUG $(BYTECCCOMPOPTS) $(IFLEXDIR)
|
||||
IFLAGS=$(CFLAGS) -DCAML_INSTR
|
||||
|
||||
OBJS=$(COMMONOBJS) $(UNIX_OR_WIN32).o main.o
|
||||
DOBJS=$(OBJS:.o=.d.o) instrtrace.d.o
|
||||
IOBJS=$(OBJS:.o=.i.o)
|
||||
PICOBJS=$(OBJS:.o=.pic.o)
|
||||
|
||||
all:: all-$(SHARED)
|
||||
|
@ -30,6 +32,9 @@ ocamlrund$(EXE): libcamlrund.a prims.o
|
|||
$(MKEXE) $(MKEXEDEBUGFLAG) $(BYTECCLINKOPTS) -o ocamlrund$(EXE) \
|
||||
prims.o libcamlrund.a $(BYTECCLIBS)
|
||||
|
||||
ocamlruni$(EXE): prims.o libcamlruni.a
|
||||
$(MKEXE) $(BYTECCLINKOPTS) -o $@ $^ $(BYTECCLIBS)
|
||||
|
||||
libcamlrun.a: $(OBJS)
|
||||
$(ARCMD) rc libcamlrun.a $(OBJS)
|
||||
$(RANLIB) libcamlrun.a
|
||||
|
@ -38,6 +43,10 @@ libcamlrund.a: $(DOBJS)
|
|||
$(ARCMD) rc libcamlrund.a $(DOBJS)
|
||||
$(RANLIB) libcamlrund.a
|
||||
|
||||
libcamlruni.a: $(IOBJS)
|
||||
$(ARCMD) rc $@ $^
|
||||
$(RANLIB) $@
|
||||
|
||||
all-noshared:
|
||||
.PHONY: all-noshared
|
||||
|
||||
|
@ -68,12 +77,18 @@ clean::
|
|||
%.d.o: %.c
|
||||
$(CC) -c $(DFLAGS) $< -o $@
|
||||
|
||||
%.i.o: %.c
|
||||
$(CC) -c $(IFLAGS) -o $@ $<
|
||||
|
||||
%.pic.o: %.c
|
||||
$(CC) -c $(CFLAGS) $(SHAREDCCCOMPOPTS) $< -o $@
|
||||
|
||||
depend : prims.c caml/opnames.h caml/jumptbl.h caml/version.h
|
||||
-$(CC) -MM $(BYTECCCOMPOPTS) *.c > .depend
|
||||
-$(CC) -MM $(BYTECCCOMPOPTS) -DDEBUG *.c | sed -e 's/\.o/.d.o/' >> .depend
|
||||
-$(CC) -MM $(BYTECCCOMPOPTS) -DDEBUG *.c | sed -e 's/\.o/.d.o/' \
|
||||
>> .depend
|
||||
-$(CC) -MM $(BYTECCCOMPOPTS) -DCAML_INSTR *.c | sed -e 's/\.o/.i.o/' \
|
||||
>> .depend
|
||||
-$(CC) -MM $(BYTECCCOMPOPTS) *.c | sed -e 's/\.o/.pic.o/' >> .depend
|
||||
.PHONY: depend
|
||||
|
||||
|
|
|
@ -48,6 +48,10 @@ all-noruntimed:
|
|||
all-runtimed: ocamlrund$(EXE) libcamlrund.$(A)
|
||||
.PHONY: all-runtimed
|
||||
|
||||
ifeq "$(RUNTIMEI)" "true"
|
||||
all:: ocamlruni$(EXE) libcamlruni.$(A)
|
||||
endif
|
||||
|
||||
ld.conf: ../config/Makefile
|
||||
echo "$(STUBLIBDIR)" > ld.conf
|
||||
echo "$(LIBDIR)" >> ld.conf
|
||||
|
@ -84,6 +88,12 @@ install-runtimed:
|
|||
cp libcamlrund.$(A) $(INSTALL_LIBDIR)/libcamlrund.$(A)
|
||||
.PHONY: install-runtimed
|
||||
|
||||
ifeq "$(RUNTIMEI)" "true"
|
||||
install::
|
||||
cp ocamlruni$(EXE) $(INSTALL_BINDIR)/ocamlruni$(EXE)
|
||||
cp libcamlruni.$(A) $(INSTALL_LIBDIR)/libcamlruni.$(A)
|
||||
endif
|
||||
|
||||
# If primitives contain duplicated lines (e.g. because the code is defined
|
||||
# like
|
||||
# #ifdef X
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "caml/memory.h"
|
||||
#include "caml/misc.h"
|
||||
#include "caml/mlvalues.h"
|
||||
#include "caml/signals.h"
|
||||
|
||||
/* returns number of elements (either fields or floats) */
|
||||
CAMLexport mlsize_t caml_array_length(value array)
|
||||
|
@ -181,12 +182,16 @@ CAMLprim value caml_make_vect(value len, value init)
|
|||
}
|
||||
} else {
|
||||
if (size > Max_wosize) caml_invalid_argument("Array.make");
|
||||
if (size < Max_young_wosize) {
|
||||
if (size <= Max_young_wosize) {
|
||||
res = caml_alloc_small(size, 0);
|
||||
for (i = 0; i < size; i++) Field(res, i) = init;
|
||||
}
|
||||
else if (Is_block(init) && Is_young(init)) {
|
||||
caml_minor_collection();
|
||||
/* We don't want to create so many major-to-minor references,
|
||||
so [init] is moved to the major heap by doing a minor GC. */
|
||||
CAML_INSTR_INT ("force_minor/make_vect@", 1);
|
||||
caml_request_minor_gc ();
|
||||
caml_gc_dispatch ();
|
||||
res = caml_alloc_shr(size, 0);
|
||||
for (i = 0; i < size; i++) Field(res, i) = init;
|
||||
res = caml_check_urgent_gc (res);
|
||||
|
@ -324,7 +329,7 @@ static value caml_array_gather(intnat num_arrays,
|
|||
/* Array of values, too big. */
|
||||
caml_invalid_argument("Array.concat");
|
||||
}
|
||||
else if (size < Max_young_wosize) {
|
||||
else if (size <= Max_young_wosize) {
|
||||
/* Array of values, small enough to fit in young generation.
|
||||
We can use memcpy directly. */
|
||||
res = caml_alloc_small(size, 0);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#ifndef CAML_ADDRESS_CLASS_H
|
||||
#define CAML_ADDRESS_CLASS_H
|
||||
|
||||
#include "config.h"
|
||||
#include "misc.h"
|
||||
#include "mlvalues.h"
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@ typedef uint64_t uintnat;
|
|||
#define ARCH_FLOAT_ENDIANNESS 0x01234567
|
||||
#endif
|
||||
|
||||
|
||||
/* We use threaded code interpretation if the compiler provides labels
|
||||
as first-class values (GCC 2.x). */
|
||||
|
||||
|
@ -143,10 +144,11 @@ typedef uint64_t uintnat;
|
|||
/* Maximum size of a block allocated in the young generation (words). */
|
||||
/* Must be > 4 */
|
||||
#define Max_young_wosize 256
|
||||
#define Max_young_whsize (Whsize_wosize (Max_young_wosize))
|
||||
|
||||
|
||||
/* Minimum size of the minor zone (words).
|
||||
This must be at least [Max_young_wosize + 1]. */
|
||||
This must be at least [2 * Max_young_whsize]. */
|
||||
#define Minor_heap_min 4096
|
||||
|
||||
/* Maximum size of the minor zone (words).
|
||||
|
@ -185,5 +187,12 @@ typedef uint64_t uintnat;
|
|||
*/
|
||||
#define Max_percent_free_def 500
|
||||
|
||||
/* Default setting for the major GC slice smoothing window: 1
|
||||
(i.e. no smoothing)
|
||||
*/
|
||||
#define Major_window_def 1
|
||||
|
||||
/* Maximum size of the major GC slice smoothing window. */
|
||||
#define Max_major_window 50
|
||||
|
||||
#endif /* CAML_CONFIG_H */
|
||||
|
|
|
@ -31,8 +31,16 @@ extern intnat
|
|||
|
||||
uintnat caml_normalize_heap_increment (uintnat);
|
||||
|
||||
void caml_init_gc (uintnat, uintnat, uintnat,
|
||||
uintnat, uintnat);
|
||||
/*
|
||||
minor_size: cf. minor_heap_size in gc.mli
|
||||
major_size: Size in words of the initial major heap
|
||||
major_incr: cf. major_heap_increment in gc.mli
|
||||
percent_fr: cf. space_overhead in gc.mli
|
||||
percent_m : cf. max_overhead in gc.mli
|
||||
window : cf. window_size in gc.mli
|
||||
*/
|
||||
void caml_init_gc (uintnat minor_size, uintnat major_size, uintnat major_incr,
|
||||
uintnat percent_fr, uintnat percent_m, uintnat window);
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "mlvalues.h"
|
||||
#include "misc.h"
|
||||
|
||||
extern int caml_trace_flag;
|
||||
extern intnat caml_icount;
|
||||
void caml_stop_here (void);
|
||||
void caml_disasm_instr (code_t pc);
|
||||
|
|
|
@ -40,21 +40,29 @@ extern uintnat caml_fl_wsz_at_phase_change;
|
|||
#define Phase_mark 0
|
||||
#define Phase_sweep 1
|
||||
#define Phase_idle 2
|
||||
#define Subphase_main 10
|
||||
#define Subphase_weak1 11
|
||||
#define Subphase_weak2 12
|
||||
#define Subphase_final 13
|
||||
#define Subphase_roots 10
|
||||
#define Subphase_main 11
|
||||
#define Subphase_weak1 12
|
||||
#define Subphase_weak2 13
|
||||
#define Subphase_final 14
|
||||
|
||||
CAMLextern char *caml_heap_start;
|
||||
extern uintnat total_heap_size;
|
||||
extern char *caml_gc_sweep_hp;
|
||||
|
||||
extern int caml_major_window;
|
||||
double caml_major_ring[Max_major_window];
|
||||
int caml_major_ring_index;
|
||||
double caml_major_work_credit;
|
||||
extern double caml_gc_clock;
|
||||
|
||||
void caml_init_major_heap (asize_t); /* size in bytes */
|
||||
asize_t caml_round_heap_chunk_wsz (asize_t);
|
||||
asize_t caml_clip_heap_chunk_wsz (asize_t wsz);
|
||||
void caml_darken (value, value *);
|
||||
intnat caml_major_collection_slice (intnat);
|
||||
void caml_major_collection_slice (intnat);
|
||||
void major_collection (void);
|
||||
void caml_finish_major_cycle (void);
|
||||
void caml_set_major_window (int);
|
||||
|
||||
|
||||
#endif /* CAML_MAJOR_GC_H */
|
||||
|
|
|
@ -44,15 +44,32 @@ CAMLextern value caml_check_urgent_gc (value);
|
|||
CAMLextern void * caml_stat_alloc (asize_t); /* Size in bytes. */
|
||||
CAMLextern void caml_stat_free (void *);
|
||||
CAMLextern void * caml_stat_resize (void *, asize_t); /* Size in bytes. */
|
||||
char *caml_alloc_for_heap (asize_t request); /* Size in bytes. */
|
||||
void caml_free_for_heap (char *mem);
|
||||
int caml_add_to_heap (char *mem);
|
||||
color_t caml_allocation_color (void *hp);
|
||||
CAMLextern int caml_init_alloc_for_heap (void);
|
||||
CAMLextern char *caml_alloc_for_heap (asize_t request); /* Size in bytes. */
|
||||
CAMLextern void caml_free_for_heap (char *mem);
|
||||
CAMLextern int caml_add_to_heap (char *mem);
|
||||
CAMLextern color_t caml_allocation_color (void *hp);
|
||||
|
||||
CAMLextern int caml_huge_fallback_count;
|
||||
|
||||
/* void caml_shrink_heap (char *); Only used in compact.c */
|
||||
|
||||
/* <private> */
|
||||
|
||||
extern uintnat caml_use_huge_pages;
|
||||
|
||||
#ifdef HAS_HUGE_PAGES
|
||||
#include <sys/mman.h>
|
||||
#define Heap_page_size HUGE_PAGE_SIZE
|
||||
#define Round_mmap_size(x) \
|
||||
(((x) + (Heap_page_size - 1)) & ~ (Heap_page_size - 1))
|
||||
#endif
|
||||
|
||||
|
||||
int caml_page_table_add(int kind, void * start, void * end);
|
||||
int caml_page_table_remove(int kind, void * start, void * end);
|
||||
int caml_page_table_initialize(mlsize_t bytesize);
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUG_clear(result, wosize) do{ \
|
||||
uintnat caml__DEBUG_i; \
|
||||
|
@ -68,10 +85,11 @@ color_t caml_allocation_color (void *hp);
|
|||
CAMLassert ((tag_t) (tag) < 256); \
|
||||
CAMLassert ((wosize) <= Max_young_wosize); \
|
||||
caml_young_ptr -= Whsize_wosize (wosize); \
|
||||
if (caml_young_ptr < caml_young_start){ \
|
||||
if (caml_young_ptr < caml_young_trigger){ \
|
||||
caml_young_ptr += Whsize_wosize (wosize); \
|
||||
CAML_INSTR_INT ("force_minor/alloc_small@", 1); \
|
||||
Setup_for_gc; \
|
||||
caml_minor_collection (); \
|
||||
caml_gc_dispatch (); \
|
||||
Restore_after_gc; \
|
||||
caml_young_ptr -= Whsize_wosize (wosize); \
|
||||
} \
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
|
||||
|
||||
#include "address_class.h"
|
||||
#include "config.h"
|
||||
|
||||
CAMLextern value *caml_young_start, *caml_young_ptr;
|
||||
CAMLextern value *caml_young_end, *caml_young_limit;
|
||||
CAMLextern value *caml_young_start, *caml_young_end;
|
||||
CAMLextern value *caml_young_alloc_start, *caml_young_alloc_end;
|
||||
CAMLextern value *caml_young_ptr, *caml_young_limit;
|
||||
CAMLextern value *caml_young_trigger;
|
||||
extern asize_t caml_minor_heap_wsz;
|
||||
extern int caml_in_minor_collection;
|
||||
|
||||
|
@ -36,7 +39,7 @@ CAMLextern struct caml_ref_table caml_ref_table, caml_weak_ref_table,
|
|||
|
||||
extern void caml_set_minor_heap_size (asize_t); /* size in bytes */
|
||||
extern void caml_empty_minor_heap (void);
|
||||
CAMLextern void caml_minor_collection (void);
|
||||
CAMLextern void caml_gc_dispatch (void);
|
||||
CAMLextern void garbage_collection (void); /* def in asmrun/signals_asm.c */
|
||||
extern void caml_realloc_ref_table (struct caml_ref_table *);
|
||||
extern void caml_alloc_table (struct caml_ref_table *, asize_t, asize_t);
|
||||
|
@ -50,4 +53,13 @@ extern void caml_oldify_mopup (void);
|
|||
} \
|
||||
}while(0)
|
||||
|
||||
static inline void add_to_ref_table (struct caml_ref_table *tbl, value *p)
|
||||
{
|
||||
if (tbl->ptr >= tbl->limit){
|
||||
CAMLassert (tbl->ptr == tbl->limit);
|
||||
caml_realloc_ref_table (tbl);
|
||||
}
|
||||
*tbl->ptr++ = p;
|
||||
}
|
||||
|
||||
#endif /* CAML_MINOR_GC_H */
|
||||
|
|
|
@ -196,11 +196,109 @@ extern void caml_set_fields (intnat v, unsigned long, unsigned long);
|
|||
|
||||
/* snprintf emulation for Win32 */
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && !defined(_UCRT)
|
||||
extern int caml_snprintf(char * buf, size_t size, const char * format, ...);
|
||||
#define snprintf caml_snprintf
|
||||
#endif
|
||||
|
||||
#ifdef CAML_INSTR
|
||||
/* Timers and counters for GC latency profiling (Linux-only) */
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern intnat caml_stat_minor_collections;
|
||||
extern intnat CAML_INSTR_STARTTIME, CAML_INSTR_STOPTIME;
|
||||
|
||||
struct CAML_INSTR_BLOCK {
|
||||
struct timespec ts[10];
|
||||
char *tag[10];
|
||||
int index;
|
||||
struct CAML_INSTR_BLOCK *next;
|
||||
};
|
||||
|
||||
extern struct CAML_INSTR_BLOCK *CAML_INSTR_LOG;
|
||||
|
||||
/* Declare a timer/counter name. [t] must be a new variable name. */
|
||||
#define CAML_INSTR_DECLARE(t) \
|
||||
struct CAML_INSTR_BLOCK *t = NULL
|
||||
|
||||
/* Allocate the data block for a given name.
|
||||
[t] must have been declared with [CAML_INSTR_DECLARE]. */
|
||||
#define CAML_INSTR_ALLOC(t) do{ \
|
||||
if (caml_stat_minor_collections >= CAML_INSTR_STARTTIME \
|
||||
&& caml_stat_minor_collections < CAML_INSTR_STOPTIME){ \
|
||||
t = malloc (sizeof (struct CAML_INSTR_BLOCK)); \
|
||||
t->index = 0; \
|
||||
t->tag[0] = ""; \
|
||||
t->next = CAML_INSTR_LOG; \
|
||||
CAML_INSTR_LOG = t; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
/* Allocate the data block and start the timer.
|
||||
[t] must have been declared with [CAML_INSTR_DECLARE]
|
||||
and allocated with [CAML_INSTR_ALLOC]. */
|
||||
#define CAML_INSTR_START(t, msg) do{ \
|
||||
if (t != NULL){ \
|
||||
t->tag[0] = msg; \
|
||||
clock_gettime (CLOCK_REALTIME, &(t->ts[0])); \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
/* Declare a timer, allocate its data, and start it.
|
||||
[t] must be a new variable name. */
|
||||
#define CAML_INSTR_SETUP(t, msg) \
|
||||
CAML_INSTR_DECLARE (t); \
|
||||
CAML_INSTR_ALLOC (t); \
|
||||
CAML_INSTR_START (t, msg)
|
||||
|
||||
/* Record an intermediate time within a given timer.
|
||||
[t] must have been declared, allocated, and started. */
|
||||
#define CAML_INSTR_TIME(t, msg) do{ \
|
||||
if (t != NULL){ \
|
||||
++ t->index; \
|
||||
t->tag[t->index] = (msg); \
|
||||
clock_gettime (CLOCK_REALTIME, &(t->ts[t->index])); \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
/* Record an integer data point.
|
||||
If [msg] ends with # it will be interpreted as an integer-valued event.
|
||||
If it ends with @ it will be interpreted as an event counter.
|
||||
*/
|
||||
#define CAML_INSTR_INT(msg, data) do{ \
|
||||
CAML_INSTR_SETUP (__caml_tmp, ""); \
|
||||
if (__caml_tmp != NULL){ \
|
||||
__caml_tmp->index = 1; \
|
||||
__caml_tmp->tag[1] = msg; \
|
||||
__caml_tmp->ts[1].tv_sec = 0; \
|
||||
__caml_tmp->ts[1].tv_nsec = (data); \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
/* This function is called at the start of the program to set up
|
||||
the data for the above macros.
|
||||
*/
|
||||
extern void CAML_INSTR_INIT (void);
|
||||
|
||||
/* This function is automatically called by the runtime to output
|
||||
the collected data to the dump file. */
|
||||
extern void CAML_INSTR_ATEXIT (void);
|
||||
|
||||
#else /* CAML_INSTR */
|
||||
|
||||
#define CAML_INSTR_DECLARE(t) /**/
|
||||
#define CAML_INSTR_ALLOC(t) /**/
|
||||
#define CAML_INSTR_START(t, name) /**/
|
||||
#define CAML_INSTR_SETUP(t, name) /**/
|
||||
#define CAML_INSTR_TIME(t, msg) /**/
|
||||
#define CAML_INSTR_INT(msg, c) /**/
|
||||
#define CAML_INSTR_INIT() /**/
|
||||
#define CAML_INSTR_ATEXIT() /**/
|
||||
|
||||
#endif /* CAML_INSTR */
|
||||
|
||||
/* </private> */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -217,7 +217,8 @@ CAMLextern value caml_hash_variant(char const * tag);
|
|||
#define Byte_u(x, i) (((unsigned char *) (x)) [i]) /* Also an l-value. */
|
||||
|
||||
/* Abstract things. Their contents is not traced by the GC; therefore they
|
||||
must not contain any [value].
|
||||
must not contain any [value]. Must have odd number so that headers with
|
||||
this tag cannot be mistaken for pointers (see caml_obj_truncate).
|
||||
*/
|
||||
#define Abstract_tag 251
|
||||
|
||||
|
@ -304,6 +305,4 @@ CAMLextern value caml_set_oo_id(value obj);
|
|||
}
|
||||
#endif
|
||||
|
||||
extern intnat caml_stat_top_heap_wsz;
|
||||
|
||||
#endif /* CAML_MLVALUES_H */
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
typedef void (*scanning_action) (value, value *);
|
||||
|
||||
void caml_oldify_local_roots (void);
|
||||
void caml_darken_all_roots (void);
|
||||
void caml_do_roots (scanning_action);
|
||||
void caml_darken_all_roots_start (void);
|
||||
intnat caml_darken_all_roots_slice (intnat);
|
||||
void caml_do_roots (scanning_action, int);
|
||||
extern uintnat caml_incremental_roots_count;
|
||||
#ifndef NATIVE_CODE
|
||||
CAMLextern void caml_do_local_roots (scanning_action, value *, value *,
|
||||
struct caml__roots_block *);
|
||||
|
|
|
@ -28,14 +28,16 @@ extern "C" {
|
|||
CAMLextern intnat volatile caml_signals_are_pending;
|
||||
CAMLextern intnat volatile caml_pending_signals[];
|
||||
CAMLextern int volatile caml_something_to_do;
|
||||
extern int volatile caml_force_major_slice;
|
||||
extern int volatile caml_requested_major_slice;
|
||||
extern int volatile caml_requested_minor_gc;
|
||||
/* </private> */
|
||||
|
||||
CAMLextern void caml_enter_blocking_section (void);
|
||||
CAMLextern void caml_leave_blocking_section (void);
|
||||
|
||||
/* <private> */
|
||||
void caml_urge_major_slice (void);
|
||||
void caml_request_major_slice (void);
|
||||
void caml_request_minor_gc (void);
|
||||
CAMLextern int caml_convert_signal_number (int);
|
||||
CAMLextern int caml_rev_convert_signal_number (int);
|
||||
void caml_execute_signal(int signal_number, int in_signal_handler);
|
||||
|
|
|
@ -21,6 +21,7 @@ extern uintnat caml_init_minor_heap_wsz;
|
|||
extern uintnat caml_init_heap_chunk_sz;
|
||||
extern uintnat caml_init_heap_wsz;
|
||||
extern uintnat caml_init_max_stack_wsz;
|
||||
extern uintnat caml_init_major_window;
|
||||
extern uintnat caml_trace_level;
|
||||
|
||||
extern void caml_parse_ocamlrunparam (void);
|
||||
|
|
|
@ -185,7 +185,7 @@ static void do_compaction (void)
|
|||
/* Invert roots first because the threads library needs some heap
|
||||
data structures to find its roots. Fortunately, it doesn't need
|
||||
the headers (see above). */
|
||||
caml_do_roots (invert_root);
|
||||
caml_do_roots (invert_root, 1);
|
||||
caml_final_do_weak_roots (invert_root);
|
||||
|
||||
ch = caml_heap_start;
|
||||
|
@ -398,8 +398,14 @@ uintnat caml_percent_max; /* used in gc_ctrl.c and memory.c */
|
|||
void caml_compact_heap (void)
|
||||
{
|
||||
uintnat target_wsz, live;
|
||||
CAML_INSTR_SETUP(tmr, "compact");
|
||||
|
||||
CAMLassert (caml_young_ptr == caml_young_alloc_end);
|
||||
CAMLassert (caml_ref_table.ptr == caml_ref_table.base);
|
||||
CAMLassert (caml_weak_ref_table.ptr == caml_weak_ref_table.base);
|
||||
|
||||
do_compaction ();
|
||||
CAML_INSTR_TIME (tmr, "compact/main");
|
||||
/* Compaction may fail to shrink the heap to a reasonable size
|
||||
because it deals in complete chunks: if a very large chunk
|
||||
is at the beginning of the heap, everything gets moved to
|
||||
|
@ -428,8 +434,16 @@ void caml_compact_heap (void)
|
|||
live = caml_stat_heap_wsz - caml_fl_cur_wsz;
|
||||
target_wsz = live + caml_percent_free * (live / 100 + 1)
|
||||
+ Wsize_bsize (Page_size);
|
||||
target_wsz = caml_round_heap_chunk_wsz (target_wsz);
|
||||
target_wsz = caml_clip_heap_chunk_wsz (target_wsz);
|
||||
|
||||
#ifdef HAS_HUGE_PAGES
|
||||
if (caml_use_huge_pages
|
||||
&& Bsize_wsize (caml_stat_heap_wsz) <= HUGE_PAGE_SIZE)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (target_wsz < caml_stat_heap_wsz / 2){
|
||||
/* Recompact. */
|
||||
char *chunk;
|
||||
|
||||
caml_gc_message (0x10, "Recompacting heap (target=%luk words)\n",
|
||||
|
@ -456,6 +470,7 @@ void caml_compact_heap (void)
|
|||
Assert (caml_stat_heap_chunks == 1);
|
||||
Assert (Chunk_next (caml_heap_start) == NULL);
|
||||
Assert (caml_stat_heap_wsz == Wsize_bsize (Chunk_size (chunk)));
|
||||
CAML_INSTR_TIME (tmr, "compact/recompact");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,7 +488,13 @@ void caml_compact_heap_maybe (void)
|
|||
Assert (caml_gc_phase == Phase_idle);
|
||||
if (caml_percent_max >= 1000000) return;
|
||||
if (caml_stat_major_collections < 3) return;
|
||||
if (caml_stat_heap_wsz <= 2 * caml_round_heap_chunk_wsz (0)) return;
|
||||
if (caml_stat_heap_wsz <= 2 * caml_clip_heap_chunk_wsz (0)) return;
|
||||
|
||||
#ifdef HAS_HUGE_PAGES
|
||||
if (caml_use_huge_pages
|
||||
&& Bsize_wsize (caml_stat_heap_wsz) <= HUGE_PAGE_SIZE)
|
||||
return;
|
||||
#endif
|
||||
|
||||
fw = 3.0 * caml_fl_cur_wsz - 2.0 * caml_fl_wsz_at_phase_change;
|
||||
if (fw < 0) fw = caml_fl_cur_wsz;
|
||||
|
@ -492,9 +513,9 @@ void caml_compact_heap_maybe (void)
|
|||
(uintnat) fp);
|
||||
if (fp >= caml_percent_max){
|
||||
caml_gc_message (0x200, "Automatic compaction triggered.\n", 0);
|
||||
caml_empty_minor_heap (); /* minor heap must be empty for compaction */
|
||||
caml_finish_major_cycle ();
|
||||
|
||||
/* We just did a complete GC, so we can measure the overhead exactly. */
|
||||
fw = caml_fl_cur_wsz;
|
||||
fp = 100.0 * fw / (caml_stat_heap_wsz - fw);
|
||||
caml_gc_message (0x200, "Measured overhead: %"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <string.h>
|
||||
#include "caml/alloc.h"
|
||||
#include "caml/config.h"
|
||||
#include "caml/custom.h"
|
||||
#include "caml/fail.h"
|
||||
#include "caml/gc.h"
|
||||
|
@ -28,10 +29,6 @@
|
|||
#include "caml/mlvalues.h"
|
||||
#include "caml/reverse.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define inline _inline
|
||||
#endif
|
||||
|
||||
static uintnat obj_counter; /* Number of objects emitted so far */
|
||||
static uintnat size_32; /* Size in words of 32-bit block for struct. */
|
||||
static uintnat size_64; /* Size in words of 64-bit block for struct. */
|
||||
|
|
|
@ -59,16 +59,15 @@ static void alloc_to_do (int size)
|
|||
}
|
||||
}
|
||||
|
||||
/* Find white finalisable values, put them in the finalising set, and
|
||||
/* Find white finalisable values, move them to the finalising set, and
|
||||
darken them.
|
||||
The recent set is empty.
|
||||
*/
|
||||
void caml_final_update (void)
|
||||
{
|
||||
uintnat i, j, k;
|
||||
uintnat todo_count = 0;
|
||||
|
||||
Assert (young == old);
|
||||
Assert (old <= young);
|
||||
for (i = 0; i < old; i++){
|
||||
Assert (Is_block (final_table[i].val));
|
||||
Assert (Is_in_heap (final_table[i].val));
|
||||
|
@ -79,35 +78,25 @@ void caml_final_update (void)
|
|||
alloc_to_do (todo_count);
|
||||
j = k = 0;
|
||||
for (i = 0; i < old; i++){
|
||||
again:
|
||||
Assert (Is_block (final_table[i].val));
|
||||
Assert (Is_in_heap (final_table[i].val));
|
||||
Assert (Tag_val (final_table[i].val) != Forward_tag);
|
||||
if (Is_white_val (final_table[i].val)){
|
||||
if (Tag_val (final_table[i].val) == Forward_tag){
|
||||
value fv;
|
||||
Assert (final_table[i].offset == 0);
|
||||
fv = Forward_val (final_table[i].val);
|
||||
if (Is_block (fv)
|
||||
&& (!Is_in_value_area(fv) || Tag_val (fv) == Forward_tag
|
||||
|| Tag_val (fv) == Lazy_tag || Tag_val (fv) == Double_tag)){
|
||||
/* Do not short-circuit the pointer. */
|
||||
}else{
|
||||
final_table[i].val = fv;
|
||||
if (Is_block (final_table[i].val)
|
||||
&& Is_in_heap (final_table[i].val)){
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
}
|
||||
to_do_tl->item[k++] = final_table[i];
|
||||
}else{
|
||||
final_table[j++] = final_table[i];
|
||||
}
|
||||
}
|
||||
young = old = j;
|
||||
CAMLassert (i == old);
|
||||
old = j;
|
||||
for(;i < young; i++){
|
||||
final_table[j++] = final_table[i];
|
||||
}
|
||||
young = j;
|
||||
to_do_tl->size = k;
|
||||
for (i = 0; i < k; i++){
|
||||
CAMLassert (Is_white_val (to_do_tl->item[i].val));
|
||||
/* Note that item may already be dark due to multiple entries in
|
||||
the final table. */
|
||||
caml_darken (to_do_tl->item[i].val, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +113,7 @@ void caml_final_do_calls (void)
|
|||
value res;
|
||||
|
||||
if (running_finalisation_function) return;
|
||||
|
||||
if (caml_finalise_begin_hook != NULL) (*caml_finalise_begin_hook) ();
|
||||
if (to_do_hd != NULL){
|
||||
if (caml_finalise_begin_hook != NULL) (*caml_finalise_begin_hook) ();
|
||||
caml_gc_message (0x80, "Calling finalisation functions.\n", 0);
|
||||
|
@ -147,6 +136,7 @@ void caml_final_do_calls (void)
|
|||
caml_gc_message (0x80, "Done calling finalisation functions.\n", 0);
|
||||
if (caml_finalise_end_hook != NULL) (*caml_finalise_end_hook) ();
|
||||
}
|
||||
if (caml_finalise_end_hook != NULL) (*caml_finalise_end_hook) ();
|
||||
}
|
||||
|
||||
/* Call a scanning_action [f] on [x]. */
|
||||
|
@ -154,17 +144,15 @@ void caml_final_do_calls (void)
|
|||
|
||||
/* Call [*f] on the closures of the finalisable set and
|
||||
the closures and values of the finalising set.
|
||||
The recent set is empty.
|
||||
This is called by the major GC and the compactor
|
||||
through [caml_darken_all_roots].
|
||||
This is called by the major GC through [caml_darken_all_roots].
|
||||
*/
|
||||
void caml_final_do_strong_roots (scanning_action f)
|
||||
{
|
||||
uintnat i;
|
||||
struct to_do *todo;
|
||||
|
||||
Assert (old == young);
|
||||
for (i = 0; i < old; i++) Call_action (f, final_table[i].fun);
|
||||
Assert (old <= young);
|
||||
for (i = 0; i < young; i++) Call_action (f, final_table[i].fun);
|
||||
|
||||
for (todo = to_do_hd; todo != NULL; todo = todo->next){
|
||||
for (i = 0; i < todo->size; i++){
|
||||
|
@ -175,15 +163,14 @@ void caml_final_do_strong_roots (scanning_action f)
|
|||
}
|
||||
|
||||
/* Call [*f] on the values of the finalisable set.
|
||||
The recent set is empty.
|
||||
This is called directly by the compactor.
|
||||
*/
|
||||
void caml_final_do_weak_roots (scanning_action f)
|
||||
{
|
||||
uintnat i;
|
||||
|
||||
Assert (old == young);
|
||||
for (i = 0; i < old; i++) Call_action (f, final_table[i].val);
|
||||
CAMLassert (old <= young);
|
||||
for (i = 0; i < young; i++) Call_action (f, final_table[i].val);
|
||||
}
|
||||
|
||||
/* Call [*f] on the closures and values of the recent set.
|
||||
|
@ -213,9 +200,10 @@ void caml_final_empty_young (void)
|
|||
CAMLprim value caml_final_register (value f, value v)
|
||||
{
|
||||
if (!Is_block (v)
|
||||
|| !Is_in_heap_or_young(v)
|
||||
|| Tag_val (v) == Lazy_tag
|
||||
|| Tag_val (v) == Double_tag) {
|
||||
|| !Is_in_heap_or_young(v)
|
||||
|| Tag_val (v) == Lazy_tag
|
||||
|| Tag_val (v) == Double_tag
|
||||
|| Tag_val (v) == Forward_tag) {
|
||||
caml_invalid_argument ("Gc.finalise");
|
||||
}
|
||||
Assert (old <= young);
|
||||
|
|
|
@ -148,6 +148,35 @@ static header_t *allocate_block (mlsize_t wh_sz, int flpi, value prev,
|
|||
return (header_t *) &Field (cur, Wosize_hd (h) - wh_sz);
|
||||
}
|
||||
|
||||
#ifdef CAML_INSTR
|
||||
static uintnat instr_size [20] =
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
static char *instr_name [20] = {
|
||||
NULL,
|
||||
"alloc01@",
|
||||
"alloc02@",
|
||||
"alloc03@",
|
||||
"alloc04@",
|
||||
"alloc05@",
|
||||
"alloc06@",
|
||||
"alloc07@",
|
||||
"alloc08@",
|
||||
"alloc09@",
|
||||
"alloc10-19@",
|
||||
"alloc20-29@",
|
||||
"alloc30-39@",
|
||||
"alloc40-49@",
|
||||
"alloc50-59@",
|
||||
"alloc60-69@",
|
||||
"alloc70-79@",
|
||||
"alloc80-89@",
|
||||
"alloc90-99@",
|
||||
"alloc_large@",
|
||||
};
|
||||
uintnat caml_instr_alloc_jump = 0;
|
||||
/* number of pointers followed to allocate from the free list */
|
||||
#endif /*CAML_INSTR*/
|
||||
|
||||
/* [caml_fl_allocate] does not set the header of the newly allocated block.
|
||||
The calling function must do it before any GC function gets called.
|
||||
[caml_fl_allocate] returns a head pointer.
|
||||
|
@ -160,6 +189,16 @@ header_t *caml_fl_allocate (mlsize_t wo_sz)
|
|||
mlsize_t sz, prevsz;
|
||||
Assert (sizeof (char *) == sizeof (value));
|
||||
Assert (wo_sz >= 1);
|
||||
#ifdef CAML_INSTR
|
||||
if (wo_sz < 10){
|
||||
++instr_size[wo_sz];
|
||||
}else if (wo_sz < 100){
|
||||
++instr_size[wo_sz/10 + 9];
|
||||
}else{
|
||||
++instr_size[19];
|
||||
}
|
||||
#endif /* CAML_INSTR */
|
||||
|
||||
switch (policy){
|
||||
case Policy_next_fit:
|
||||
Assert (fl_prev != Val_NULL);
|
||||
|
@ -172,6 +211,9 @@ header_t *caml_fl_allocate (mlsize_t wo_sz)
|
|||
}
|
||||
prev = cur;
|
||||
cur = Next (prev);
|
||||
#ifdef CAML_INSTR
|
||||
++ caml_instr_alloc_jump;
|
||||
#endif
|
||||
}
|
||||
fl_last = prev;
|
||||
/* Search from the start of the list to [fl_prev]. */
|
||||
|
@ -183,6 +225,9 @@ header_t *caml_fl_allocate (mlsize_t wo_sz)
|
|||
}
|
||||
prev = cur;
|
||||
cur = Next (prev);
|
||||
#ifdef CAML_INSTR
|
||||
++ caml_instr_alloc_jump;
|
||||
#endif
|
||||
}
|
||||
/* No suitable block was found. */
|
||||
return NULL;
|
||||
|
@ -347,6 +392,13 @@ static header_t *last_fragment;
|
|||
|
||||
void caml_fl_init_merge (void)
|
||||
{
|
||||
#ifdef CAML_INSTR
|
||||
int i;
|
||||
for (i = 1; i < 20; i++){
|
||||
CAML_INSTR_INT (instr_name[i], instr_size[i]);
|
||||
instr_size[i] = 0;
|
||||
}
|
||||
#endif /* CAML_INSTR */
|
||||
last_fragment = NULL;
|
||||
caml_fl_merge = Fl_head;
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -12,21 +12,26 @@
|
|||
/***********************************************************************/
|
||||
|
||||
#include "caml/alloc.h"
|
||||
#include "caml/backtrace.h"
|
||||
#include "caml/compact.h"
|
||||
#include "caml/custom.h"
|
||||
#include "caml/fail.h"
|
||||
#include "caml/finalise.h"
|
||||
#include "caml/freelist.h"
|
||||
#include "caml/gc.h"
|
||||
#include "caml/gc_ctrl.h"
|
||||
#include "caml/major_gc.h"
|
||||
#include "caml/memory.h"
|
||||
#include "caml/minor_gc.h"
|
||||
#include "caml/misc.h"
|
||||
#include "caml/mlvalues.h"
|
||||
#include "caml/signals.h"
|
||||
#ifdef NATIVE_CODE
|
||||
#include "stack.h"
|
||||
#else
|
||||
#include "caml/stacks.h"
|
||||
#endif
|
||||
#include "caml/startup_aux.h"
|
||||
|
||||
#ifndef NATIVE_CODE
|
||||
extern uintnat caml_max_stack_size; /* defined in stacks.c */
|
||||
|
@ -214,7 +219,7 @@ static value heap_stats (int returnstats)
|
|||
|
||||
/* get a copy of these before allocating anything... */
|
||||
double minwords = caml_stat_minor_words
|
||||
+ (double) (caml_young_end - caml_young_ptr);
|
||||
+ (double) (caml_young_alloc_end - caml_young_ptr);
|
||||
double prowords = caml_stat_promoted_words;
|
||||
double majwords = caml_stat_major_words + (double) caml_allocated_words;
|
||||
intnat mincoll = caml_stat_minor_collections;
|
||||
|
@ -255,8 +260,12 @@ void caml_heap_check (void)
|
|||
|
||||
CAMLprim value caml_gc_stat(value v)
|
||||
{
|
||||
value result;
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
Assert (v == Val_unit);
|
||||
return heap_stats (1);
|
||||
result = heap_stats (1);
|
||||
CAML_INSTR_TIME (tmr, "explicit/gc_stat");
|
||||
return result;
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_quick_stat(value v)
|
||||
|
@ -266,7 +275,7 @@ CAMLprim value caml_gc_quick_stat(value v)
|
|||
|
||||
/* get a copy of these before allocating anything... */
|
||||
double minwords = caml_stat_minor_words
|
||||
+ (double) (caml_young_end - caml_young_ptr);
|
||||
+ (double) (caml_young_alloc_end - caml_young_ptr);
|
||||
double prowords = caml_stat_promoted_words;
|
||||
double majwords = caml_stat_major_words + (double) caml_allocated_words;
|
||||
intnat mincoll = caml_stat_minor_collections;
|
||||
|
@ -303,7 +312,7 @@ CAMLprim value caml_gc_counters(value v)
|
|||
|
||||
/* get a copy of these before allocating anything... */
|
||||
double minwords = caml_stat_minor_words
|
||||
+ (double) (caml_young_end - caml_young_ptr);
|
||||
+ (double) (caml_young_alloc_end - caml_young_ptr);
|
||||
double prowords = caml_stat_promoted_words;
|
||||
double majwords = caml_stat_major_words + (double) caml_allocated_words;
|
||||
|
||||
|
@ -314,12 +323,17 @@ CAMLprim value caml_gc_counters(value v)
|
|||
CAMLreturn (res);
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_huge_fallback_count (value v)
|
||||
{
|
||||
return Val_long (caml_huge_fallback_count);
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_get(value v)
|
||||
{
|
||||
CAMLparam0 (); /* v is ignored */
|
||||
CAMLlocal1 (res);
|
||||
|
||||
res = caml_alloc_tuple (7);
|
||||
res = caml_alloc_tuple (8);
|
||||
Store_field (res, 0, Val_long (caml_minor_heap_wsz)); /* s */
|
||||
Store_field (res, 1, Val_long (caml_major_heap_increment)); /* i */
|
||||
Store_field (res, 2, Val_long (caml_percent_free)); /* o */
|
||||
|
@ -331,6 +345,7 @@ CAMLprim value caml_gc_get(value v)
|
|||
Store_field (res, 5, Val_long (0));
|
||||
#endif
|
||||
Store_field (res, 6, Val_long (caml_allocation_policy)); /* a */
|
||||
Store_field (res, 7, Val_long (caml_major_window)); /* w */
|
||||
CAMLreturn (res);
|
||||
}
|
||||
|
||||
|
@ -353,12 +368,20 @@ static intnat norm_minsize (intnat s)
|
|||
return s;
|
||||
}
|
||||
|
||||
static uintnat norm_window (intnat w)
|
||||
{
|
||||
if (w < 1) w = 1;
|
||||
if (w > Max_major_window) w = Max_major_window;
|
||||
return w;
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_set(value v)
|
||||
{
|
||||
uintnat newpf, newpm;
|
||||
asize_t newheapincr;
|
||||
asize_t newminwsz;
|
||||
uintnat oldpolicy;
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
|
||||
caml_verb_gc = Long_val (Field (v, 3));
|
||||
|
||||
|
@ -396,6 +419,16 @@ CAMLprim value caml_gc_set(value v)
|
|||
caml_allocation_policy);
|
||||
}
|
||||
|
||||
/* This field was added in 4.03.0. */
|
||||
if (Wosize_val (v) >= 8){
|
||||
int old_window = caml_major_window;
|
||||
caml_set_major_window (norm_window (Long_val (Field (v, 7))));
|
||||
if (old_window != caml_major_window){
|
||||
caml_gc_message (0x20, "New smoothing window size: %d\n",
|
||||
caml_major_window);
|
||||
}
|
||||
}
|
||||
|
||||
/* Minor heap size comes last because it will trigger a minor collection
|
||||
(thus invalidating [v]) and it can raise [Out_of_memory]. */
|
||||
newminwsz = norm_minsize (Long_val (Field (v, 0)));
|
||||
|
@ -404,12 +437,17 @@ CAMLprim value caml_gc_set(value v)
|
|||
newminwsz / 1024);
|
||||
caml_set_minor_heap_size (Bsize_wsize (newminwsz));
|
||||
}
|
||||
CAML_INSTR_TIME (tmr, "explicit/gc_set");
|
||||
return Val_unit;
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_minor(value v)
|
||||
{ Assert (v == Val_unit);
|
||||
caml_minor_collection ();
|
||||
{
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
Assert (v == Val_unit);
|
||||
caml_request_minor_gc ();
|
||||
caml_gc_dispatch ();
|
||||
CAML_INSTR_TIME (tmr, "explicit/gc_minor");
|
||||
return Val_unit;
|
||||
}
|
||||
|
||||
|
@ -429,17 +467,22 @@ static void test_and_compact (void)
|
|||
}
|
||||
|
||||
CAMLprim value caml_gc_major(value v)
|
||||
{ Assert (v == Val_unit);
|
||||
{
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
Assert (v == Val_unit);
|
||||
caml_gc_message (0x1, "Major GC cycle requested\n", 0);
|
||||
caml_empty_minor_heap ();
|
||||
caml_finish_major_cycle ();
|
||||
test_and_compact ();
|
||||
caml_final_do_calls ();
|
||||
CAML_INSTR_TIME (tmr, "explicit/gc_major");
|
||||
return Val_unit;
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_full_major(value v)
|
||||
{ Assert (v == Val_unit);
|
||||
{
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
Assert (v == Val_unit);
|
||||
caml_gc_message (0x1, "Full major GC cycle requested\n", 0);
|
||||
caml_empty_minor_heap ();
|
||||
caml_finish_major_cycle ();
|
||||
|
@ -448,18 +491,24 @@ CAMLprim value caml_gc_full_major(value v)
|
|||
caml_finish_major_cycle ();
|
||||
test_and_compact ();
|
||||
caml_final_do_calls ();
|
||||
CAML_INSTR_TIME (tmr, "explicit/gc_full_major");
|
||||
return Val_unit;
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_major_slice (value v)
|
||||
{
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
Assert (Is_long (v));
|
||||
caml_empty_minor_heap ();
|
||||
return Val_long (caml_major_collection_slice (Long_val (v)));
|
||||
caml_major_collection_slice (Long_val (v));
|
||||
CAML_INSTR_TIME (tmr, "explicit/gc_major_slice");
|
||||
return Val_long (0);
|
||||
}
|
||||
|
||||
CAMLprim value caml_gc_compaction(value v)
|
||||
{ Assert (v == Val_unit);
|
||||
{
|
||||
CAML_INSTR_SETUP (tmr, "");
|
||||
Assert (v == Val_unit);
|
||||
caml_gc_message (0x10, "Heap compaction requested\n", 0);
|
||||
caml_empty_minor_heap ();
|
||||
caml_finish_major_cycle ();
|
||||
|
@ -468,9 +517,35 @@ CAMLprim value caml_gc_compaction(value v)
|
|||
caml_finish_major_cycle ();
|
||||
caml_compact_heap ();
|
||||
caml_final_do_calls ();
|
||||
CAML_INSTR_TIME (tmr, "explicit/gc_compact");
|
||||
return Val_unit;
|
||||
}
|
||||
|
||||
CAMLprim value caml_get_minor_free (value v)
|
||||
{
|
||||
return Val_int (caml_young_ptr - caml_young_alloc_start);
|
||||
}
|
||||
|
||||
CAMLprim value caml_get_major_bucket (value v)
|
||||
{
|
||||
long i = Long_val (v);
|
||||
if (i < 0) caml_invalid_argument ("Gc.get_bucket");
|
||||
if (i < caml_major_window){
|
||||
i += caml_major_ring_index;
|
||||
if (i >= caml_major_window) i -= caml_major_window;
|
||||
CAMLassert (0 <= i && i < caml_major_window);
|
||||
return Val_long ((long) (caml_major_ring[i] * 1e6));
|
||||
}else{
|
||||
return Val_long (0);
|
||||
}
|
||||
}
|
||||
|
||||
CAMLprim value caml_get_major_credit (value v)
|
||||
{
|
||||
CAMLassert (v == Val_unit);
|
||||
return Val_long ((long) (caml_major_work_credit * 1e6));
|
||||
}
|
||||
|
||||
uintnat caml_normalize_heap_increment (uintnat i)
|
||||
{
|
||||
if (i < Bsize_wsize (Heap_chunk_min)){
|
||||
|
@ -483,11 +558,15 @@ uintnat caml_normalize_heap_increment (uintnat i)
|
|||
[major_incr] is either a percentage or a number of words */
|
||||
void caml_init_gc (uintnat minor_size, uintnat major_size,
|
||||
uintnat major_incr, uintnat percent_fr,
|
||||
uintnat percent_m)
|
||||
uintnat percent_m, uintnat window)
|
||||
{
|
||||
uintnat major_heap_size =
|
||||
Bsize_wsize (caml_normalize_heap_increment (major_size));
|
||||
|
||||
CAML_INSTR_INIT ();
|
||||
if (caml_init_alloc_for_heap () != 0){
|
||||
caml_fatal_error ("cannot initialize heap: mmap failed\n");
|
||||
}
|
||||
if (caml_page_table_initialize(Bsize_wsize(minor_size) + major_heap_size)){
|
||||
caml_fatal_error ("OCaml runtime error: cannot initialize page table\n");
|
||||
}
|
||||
|
@ -496,6 +575,7 @@ void caml_init_gc (uintnat minor_size, uintnat major_size,
|
|||
caml_percent_free = norm_pfree (percent_fr);
|
||||
caml_percent_max = norm_pmax (percent_m);
|
||||
caml_init_major_heap (major_heap_size);
|
||||
caml_major_window = norm_window (window);
|
||||
caml_gc_message (0x20, "Initial minor heap size: %luk words\n",
|
||||
caml_minor_heap_wsz / 1024);
|
||||
caml_gc_message (0x20, "Initial major heap size: %luk bytes\n",
|
||||
|
@ -511,6 +591,54 @@ void caml_init_gc (uintnat minor_size, uintnat major_size,
|
|||
}
|
||||
caml_gc_message (0x20, "Initial allocation policy: %d\n",
|
||||
caml_allocation_policy);
|
||||
caml_gc_message (0x20, "Initial smoothing window: %d\n",
|
||||
caml_major_window);
|
||||
}
|
||||
|
||||
|
||||
/* FIXME After the startup_aux.c unification, move these functions there. */
|
||||
|
||||
CAMLprim value caml_runtime_variant (value unit)
|
||||
{
|
||||
CAMLassert (unit == Val_unit);
|
||||
#if defined (DEBUG)
|
||||
return caml_copy_string ("d");
|
||||
#elif defined (CAML_INSTR)
|
||||
return caml_copy_string ("i");
|
||||
#elif defined (MMAP_INTERVAL)
|
||||
return caml_copy_string ("m");
|
||||
#else
|
||||
return caml_copy_string ("");
|
||||
#endif
|
||||
}
|
||||
|
||||
extern int caml_parser_trace;
|
||||
|
||||
CAMLprim value caml_runtime_parameters (value unit)
|
||||
{
|
||||
CAMLassert (unit == Val_unit);
|
||||
return caml_alloc_sprintf
|
||||
("a=%d,b=%s,H=%lu,i=%lu,l=%lu,o=%lu,O=%lu,p=%d,s=%lu,t=%d,v=%lu,w=%d,W=%lu",
|
||||
/* a */ caml_allocation_policy,
|
||||
/* b */ caml_backtrace_active,
|
||||
/* h */ /* missing */ /* FIXME add when changed to min_heap_size */
|
||||
/* H */ caml_use_huge_pages,
|
||||
/* i */ caml_major_heap_increment,
|
||||
#ifdef NATIVE_CODE
|
||||
/* l */ 0,
|
||||
#else
|
||||
/* l */ caml_max_stack_size,
|
||||
#endif
|
||||
/* o */ caml_percent_free,
|
||||
/* O */ caml_percent_max,
|
||||
/* p */ caml_parser_trace,
|
||||
/* R */ /* missing */
|
||||
/* s */ caml_minor_heap_wsz,
|
||||
/* t */ caml_trace_level,
|
||||
/* v */ caml_verb_gc,
|
||||
/* w */ caml_major_window,
|
||||
/* W */ caml_runtime_warnings
|
||||
);
|
||||
}
|
||||
|
||||
/* Control runtime warnings */
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "caml/instrtrace.h"
|
||||
#include "caml/instruct.h"
|
||||
#include "caml/misc.h"
|
||||
#include "caml/mlvalues.h"
|
||||
|
@ -182,19 +183,21 @@ caml_trace_value_file (value v, code_t prog, int proglen, FILE * f)
|
|||
if (prog && v % sizeof (int) == 0
|
||||
&& (code_t) v >= prog
|
||||
&& (code_t) v < (code_t) ((char *) prog + proglen))
|
||||
fprintf (f, "=code@%ld", (code_t) v - prog);
|
||||
fprintf (f, "=code@%" ARCH_INTNAT_PRINTF_FORMAT "d", (code_t) v - prog);
|
||||
else if (Is_long (v))
|
||||
fprintf (f, "=long%" ARCH_INTNAT_PRINTF_FORMAT "d", Long_val (v));
|
||||
else if ((void*)v >= (void*)caml_stack_low
|
||||
&& (void*)v < (void*)caml_stack_high)
|
||||
fprintf (f, "=stack_%ld", (intnat*)caml_stack_high - (intnat*)v);
|
||||
fprintf (f, "=stack_%" ARCH_INTNAT_PRINTF_FORMAT "d",
|
||||
(intnat*)caml_stack_high - (intnat*)v);
|
||||
else if (Is_block (v)) {
|
||||
int s = Wosize_val (v);
|
||||
int tg = Tag_val (v);
|
||||
int l = 0;
|
||||
switch (tg) {
|
||||
case Closure_tag:
|
||||
fprintf (f, "=closure[s%d,cod%ld]", s, (code_t) (Code_val (v)) - prog);
|
||||
fprintf (f, "=closure[s%d,cod%" ARCH_INTNAT_PRINTF_FORMAT "d]",
|
||||
s, (code_t) (Code_val (v)) - prog);
|
||||
goto displayfields;
|
||||
case String_tag:
|
||||
l = caml_string_length (v);
|
||||
|
@ -249,7 +252,8 @@ caml_trace_accu_sp_file (value accu, value * sp, code_t prog, int proglen,
|
|||
value *p;
|
||||
fprintf (f, "accu=");
|
||||
caml_trace_value_file (accu, prog, proglen, f);
|
||||
fprintf (f, "\n sp=%#" ARCH_INTNAT_PRINTF_FORMAT "x @%ld:",
|
||||
fprintf (f, "\n sp=%#" ARCH_INTNAT_PRINTF_FORMAT "x "
|
||||
"@%" ARCH_INTNAT_PRINTF_FORMAT "d:",
|
||||
(intnat) sp, caml_stack_high - sp);
|
||||
for (p = sp, i = 0; i < 12 + (1 << caml_trace_level) && p < caml_stack_high;
|
||||
p++, i++) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <stdio.h>
|
||||
#include "caml/alloc.h"
|
||||
#include "caml/callback.h"
|
||||
#include "caml/config.h"
|
||||
#include "caml/custom.h"
|
||||
#include "caml/fail.h"
|
||||
#include "caml/gc.h"
|
||||
|
@ -30,10 +31,6 @@
|
|||
#include "caml/misc.h"
|
||||
#include "caml/reverse.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define inline _inline
|
||||
#endif
|
||||
|
||||
static unsigned char * intern_src;
|
||||
/* Reading pointer in block holding input data. */
|
||||
|
||||
|
@ -618,8 +615,7 @@ static void intern_add_to_heap(mlsize_t whsize)
|
|||
/* Add new heap chunk to heap if needed */
|
||||
if (intern_extra_block != NULL) {
|
||||
/* If heap chunk not filled totally, build free block at end */
|
||||
asize_t request =
|
||||
((Bsize_wsize(whsize) + Page_size - 1) >> Page_log) << Page_log;
|
||||
asize_t request = Chunk_size (intern_extra_block);
|
||||
header_t * end_extra_block =
|
||||
(header_t *) intern_extra_block + Wsize_bsize(request);
|
||||
Assert(intern_block == 0);
|
||||
|
|
|
@ -26,27 +26,6 @@ CAMLextern void caml_expand_command_line (int *, char ***);
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
caml_gc_message (-1, "### OCaml runtime: debug mode ###\n", 0);
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
char *ocp;
|
||||
char *cp;
|
||||
|
||||
caml_gc_message (-1, "### command line:", 0);
|
||||
for (i = 0; i < argc; i++){
|
||||
caml_gc_message (-1, " %s", argv[i]);
|
||||
}
|
||||
caml_gc_message (-1, "\n", 0);
|
||||
ocp = getenv ("OCAMLRUNPARAM");
|
||||
caml_gc_message (-1, "### OCAMLRUNPARAM=%s\n", ocp == NULL ? "" : ocp);
|
||||
cp = getenv ("CAMLRUNPARAM");
|
||||
caml_gc_message (-1, "### CAMLRUNPARAM=%s\n", cp == NULL ? "" : cp);
|
||||
caml_gc_message (-1, "### working dir: %s\n", getcwd (NULL, 0));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
/* Expand wildcards and diversions in command line */
|
||||
caml_expand_command_line(&argc, &argv);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
/***********************************************************************/
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "caml/compact.h"
|
||||
#include "caml/custom.h"
|
||||
|
@ -33,6 +34,12 @@
|
|||
#undef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
static inline double fmin(double a, double b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
#endif
|
||||
|
||||
uintnat caml_percent_free;
|
||||
uintnat caml_major_heap_increment;
|
||||
CAMLexport char *caml_heap_start;
|
||||
|
@ -55,6 +62,12 @@ static char *markhp, *chunk, *limit;
|
|||
int caml_gc_subphase; /* Subphase_{main,weak1,weak2,final} */
|
||||
static value *weak_prev;
|
||||
|
||||
int caml_major_window = 1;
|
||||
double caml_major_ring[Max_major_window] = { 0. };
|
||||
int caml_major_ring_index = 0;
|
||||
double caml_major_work_credit = 0.0;
|
||||
double caml_gc_clock = 0.0;
|
||||
|
||||
#ifdef DEBUG
|
||||
static unsigned long major_gc_counter = 0;
|
||||
#endif
|
||||
|
@ -91,7 +104,7 @@ static void realloc_gray_vals (void)
|
|||
void caml_darken (value v, value *p /* not used */)
|
||||
{
|
||||
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
||||
if (Is_block (v) && Wosize_val (v) > 0) {
|
||||
if (Is_block (v) && !Is_young (v) && Wosize_val (v) > 0) {
|
||||
#else
|
||||
if (Is_block (v) && Is_in_heap (v)) {
|
||||
#endif
|
||||
|
@ -129,9 +142,9 @@ static void start_cycle (void)
|
|||
Assert (caml_gc_phase == Phase_idle);
|
||||
Assert (gray_vals_cur == gray_vals);
|
||||
caml_gc_message (0x01, "Starting new major GC cycle\n", 0);
|
||||
caml_darken_all_roots();
|
||||
caml_darken_all_roots_start ();
|
||||
caml_gc_phase = Phase_mark;
|
||||
caml_gc_subphase = Subphase_main;
|
||||
caml_gc_subphase = Subphase_roots;
|
||||
markhp = NULL;
|
||||
#ifdef DEBUG
|
||||
++ major_gc_counter;
|
||||
|
@ -139,35 +152,65 @@ static void start_cycle (void)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* We may stop the slice inside values, in order to avoid large latencies
|
||||
on large arrays. In this case, [current_value] is the partially-marked
|
||||
value and [current_index] is the index of the next field to be marked.
|
||||
*/
|
||||
static value current_value = 0;
|
||||
static mlsize_t current_index = 0;
|
||||
|
||||
#ifdef CAML_INSTR
|
||||
#define INSTR(x) x
|
||||
#else
|
||||
#define INSTR(x) /**/
|
||||
#endif
|
||||
|
||||
static void mark_slice (intnat work)
|
||||
{
|
||||
value *gray_vals_ptr; /* Local copy of gray_vals_cur */
|
||||
value *gray_vals_ptr; /* Local copy of [gray_vals_cur] */
|
||||
value v, child;
|
||||
header_t hd;
|
||||
mlsize_t size, i;
|
||||
header_t hd, chd;
|
||||
mlsize_t size, i, start, end; /* [start] is a local copy of [current_index] */
|
||||
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
||||
int marking_closure = 0;
|
||||
#endif
|
||||
#ifdef CAML_INSTR
|
||||
int slice_fields = 0;
|
||||
int slice_pointers = 0;
|
||||
#endif
|
||||
|
||||
caml_gc_message (0x40, "Marking %ld words\n", work);
|
||||
caml_gc_message (0x40, "Subphase = %ld\n", caml_gc_subphase);
|
||||
gray_vals_ptr = gray_vals_cur;
|
||||
v = current_value;
|
||||
start = current_index;
|
||||
while (work > 0){
|
||||
if (gray_vals_ptr > gray_vals){
|
||||
if (v == 0 && gray_vals_ptr > gray_vals){
|
||||
CAMLassert (start == 0);
|
||||
v = *--gray_vals_ptr;
|
||||
CAMLassert (Is_gray_val (v));
|
||||
}
|
||||
if (v != 0){
|
||||
hd = Hd_val(v);
|
||||
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
||||
marking_closure =
|
||||
(Tag_hd (hd) == Closure_tag || Tag_hd (hd) == Infix_tag);
|
||||
#endif
|
||||
Assert (Is_gray_hd (hd));
|
||||
Hd_val (v) = Blackhd_hd (hd);
|
||||
size = Wosize_hd (hd);
|
||||
end = start + work;
|
||||
if (Tag_hd (hd) < No_scan_tag){
|
||||
for (i = 0; i < size; i++){
|
||||
start = size < start ? size : start;
|
||||
end = size < end ? size : end;
|
||||
CAMLassert (end > start);
|
||||
INSTR (slice_fields += end - start;)
|
||||
INSTR (if (size > end)
|
||||
CAML_INSTR_INT ("major/mark/slice/remain", size - end);)
|
||||
for (i = start; i < end; i++){
|
||||
child = Field (v, i);
|
||||
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
||||
if (Is_block (child)
|
||||
&& ! Is_young (child)
|
||||
&& Wosize_val (child) > 0 /* Atoms never need to be marked. */
|
||||
/* Closure blocks contain code pointers at offsets that cannot
|
||||
be reliably determined, so we always use the page table when
|
||||
|
@ -176,8 +219,9 @@ static void mark_slice (intnat work)
|
|||
#else
|
||||
if (Is_block (child) && Is_in_heap (child)) {
|
||||
#endif
|
||||
hd = Hd_val (child);
|
||||
if (Tag_hd (hd) == Forward_tag){
|
||||
INSTR (++ slice_pointers;)
|
||||
chd = Hd_val (child);
|
||||
if (Tag_hd (chd) == Forward_tag){
|
||||
value f = Forward_val (child);
|
||||
if (Is_block (f)
|
||||
&& (!Is_in_value_area(f) || Tag_val (f) == Forward_tag
|
||||
|
@ -185,18 +229,19 @@ static void mark_slice (intnat work)
|
|||
/* Do not short-circuit the pointer. */
|
||||
}else{
|
||||
Field (v, i) = f;
|
||||
if (Is_block (f) && Is_young (f) && !Is_young (child))
|
||||
add_to_ref_table (&caml_ref_table, &Field (v, i));
|
||||
}
|
||||
}
|
||||
else if (Tag_hd(hd) == Infix_tag) {
|
||||
}else if (Tag_hd(chd) == Infix_tag) {
|
||||
child -= Infix_offset_val(child);
|
||||
hd = Hd_val(child);
|
||||
chd = Hd_val(child);
|
||||
}
|
||||
#ifdef NATIVE_CODE_AND_NO_NAKED_POINTERS
|
||||
/* See [caml_darken] for a description of this assertion. */
|
||||
CAMLassert (Is_in_heap (child) || Is_black_hd (hd));
|
||||
CAMLassert (Is_in_heap (child) || Is_black_hd (chd));
|
||||
#endif
|
||||
if (Is_white_hd (hd)){
|
||||
Hd_val (child) = Grayhd_hd (hd);
|
||||
if (Is_white_hd (chd)){
|
||||
Hd_val (child) = Grayhd_hd (chd);
|
||||
*gray_vals_ptr++ = child;
|
||||
if (gray_vals_ptr >= gray_vals_end) {
|
||||
gray_vals_cur = gray_vals_ptr;
|
||||
|
@ -206,8 +251,25 @@ static void mark_slice (intnat work)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (end < size){
|
||||
work = 0;
|
||||
start = end;
|
||||
/* [v] doesn't change. */
|
||||
CAMLassert (Is_gray_val (v));
|
||||
}else{
|
||||
CAMLassert (end == size);
|
||||
Hd_val (v) = Blackhd_hd (hd);
|
||||
work -= Whsize_wosize(end - start);
|
||||
start = 0;
|
||||
v = 0;
|
||||
}
|
||||
}else{
|
||||
/* The block doesn't contain any pointers. */
|
||||
CAMLassert (start == 0);
|
||||
Hd_val (v) = Blackhd_hd (hd);
|
||||
work -= Whsize_wosize(size);
|
||||
v = 0;
|
||||
}
|
||||
work -= Whsize_wosize(size);
|
||||
}else if (markhp != NULL){
|
||||
if (markhp == limit){
|
||||
chunk = Chunk_next (chunk);
|
||||
|
@ -220,7 +282,8 @@ static void mark_slice (intnat work)
|
|||
}else{
|
||||
if (Is_gray_val (Val_hp (markhp))){
|
||||
Assert (gray_vals_ptr == gray_vals);
|
||||
*gray_vals_ptr++ = Val_hp (markhp);
|
||||
CAMLassert (v == 0 && start == 0);
|
||||
v = Val_hp (markhp);
|
||||
}
|
||||
markhp += Bhsize_hp (markhp);
|
||||
}
|
||||
|
@ -231,6 +294,15 @@ static void mark_slice (intnat work)
|
|||
limit = chunk + Chunk_size (chunk);
|
||||
}else{
|
||||
switch (caml_gc_subphase){
|
||||
case Subphase_roots: {
|
||||
gray_vals_cur = gray_vals_ptr;
|
||||
work = caml_darken_all_roots_slice (work);
|
||||
gray_vals_ptr = gray_vals_cur;
|
||||
if (work > 0){
|
||||
caml_gc_subphase = Subphase_main;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Subphase_main: {
|
||||
/* The main marking phase is over. Start removing weak pointers to
|
||||
dead values. */
|
||||
|
@ -251,7 +323,7 @@ static void mark_slice (intnat work)
|
|||
curfield = Field (cur, i);
|
||||
weak_again:
|
||||
if (curfield != caml_weak_none
|
||||
&& Is_block (curfield) && Is_in_heap (curfield)){
|
||||
&& Is_block (curfield) && Is_in_heap_or_young (curfield)){
|
||||
if (Tag_val (curfield) == Forward_tag){
|
||||
value f = Forward_val (curfield);
|
||||
if (Is_block (f)) {
|
||||
|
@ -260,11 +332,13 @@ static void mark_slice (intnat work)
|
|||
/* Do not short-circuit the pointer. */
|
||||
}else{
|
||||
Field (cur, i) = curfield = f;
|
||||
if (Is_block (f) && Is_young (f))
|
||||
add_to_ref_table (&caml_weak_ref_table, &Field (cur, i));
|
||||
goto weak_again;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Is_white_val (curfield)){
|
||||
if (Is_white_val (curfield) && !Is_young (curfield)){
|
||||
Field (cur, i) = caml_weak_none;
|
||||
}
|
||||
}
|
||||
|
@ -277,6 +351,10 @@ static void mark_slice (intnat work)
|
|||
gray_vals_cur = gray_vals_ptr;
|
||||
caml_final_update ();
|
||||
gray_vals_ptr = gray_vals_cur;
|
||||
if (gray_vals_ptr > gray_vals){
|
||||
v = *--gray_vals_ptr;
|
||||
CAMLassert (start == 0);
|
||||
}
|
||||
caml_gc_subphase = Subphase_weak2;
|
||||
weak_prev = &caml_weak_list_head;
|
||||
}
|
||||
|
@ -304,7 +382,6 @@ static void mark_slice (intnat work)
|
|||
break;
|
||||
case Subphase_final: {
|
||||
/* Initialise the sweep phase. */
|
||||
gray_vals_cur = gray_vals_ptr;
|
||||
caml_gc_sweep_hp = caml_heap_start;
|
||||
caml_fl_init_merge ();
|
||||
caml_gc_phase = Phase_sweep;
|
||||
|
@ -321,6 +398,10 @@ static void mark_slice (intnat work)
|
|||
}
|
||||
}
|
||||
gray_vals_cur = gray_vals_ptr;
|
||||
current_value = v;
|
||||
current_index = start;
|
||||
INSTR (CAML_INSTR_INT ("major/mark/slice/fields#", slice_fields);)
|
||||
INSTR (CAML_INSTR_INT ("major/mark/slice/pointers#", slice_pointers);)
|
||||
}
|
||||
|
||||
static void sweep_slice (intnat work)
|
||||
|
@ -368,14 +449,37 @@ static void sweep_slice (intnat work)
|
|||
}
|
||||
}
|
||||
|
||||
/* The main entry point for the GC. Called after each minor GC.
|
||||
[howmuch] is the amount of work to do, 0 to let the GC compute it.
|
||||
Return the computed amount of work to do.
|
||||
#ifdef CAML_INSTR
|
||||
static char *mark_slice_name[] = {
|
||||
/* 0 */ NULL,
|
||||
/* 1 */ NULL,
|
||||
/* 2 */ NULL,
|
||||
/* 3 */ NULL,
|
||||
/* 4 */ NULL,
|
||||
/* 5 */ NULL,
|
||||
/* 6 */ NULL,
|
||||
/* 7 */ NULL,
|
||||
/* 8 */ NULL,
|
||||
/* 9 */ NULL,
|
||||
/* 10 */ "major/mark_roots",
|
||||
/* 11 */ "major/mark_main",
|
||||
/* 12 */ "major/mark_weak1",
|
||||
/* 13 */ "major/mark_weak2",
|
||||
/* 14 */ "major/mark_final",
|
||||
};
|
||||
#endif
|
||||
|
||||
/* The main entry point for the major GC. Called about once for each
|
||||
minor GC. [howmuch] is the amount of work to do:
|
||||
-1 if the GC is triggered automatically
|
||||
0 to let the GC compute the amount of work
|
||||
[n] to make the GC do enough work to (on average) free [n] words
|
||||
*/
|
||||
intnat caml_major_collection_slice (intnat howmuch)
|
||||
void caml_major_collection_slice (intnat howmuch)
|
||||
{
|
||||
double p, dp;
|
||||
double p, dp, filt_p, spend;
|
||||
intnat computed_work;
|
||||
int i;
|
||||
/*
|
||||
Free memory at the start of the GC cycle (garbage + free list) (assumed):
|
||||
FM = caml_stat_heap_wsz * caml_percent_free
|
||||
|
@ -395,24 +499,37 @@ intnat caml_major_collection_slice (intnat howmuch)
|
|||
PE = caml_extra_heap_resources
|
||||
Proportion of total work to do in this slice:
|
||||
P = max (PH, PE)
|
||||
|
||||
Here, we insert a time-based filter on the P variable to avoid large
|
||||
latency spikes in the GC, so the P below is a smoothed-out version of
|
||||
the P above.
|
||||
|
||||
Amount of marking work for the GC cycle:
|
||||
MW = caml_stat_heap_wsz * 100 / (100 + caml_percent_free)
|
||||
+ caml_incremental_roots_count
|
||||
Amount of sweeping work for the GC cycle:
|
||||
SW = caml_stat_heap_wsz
|
||||
|
||||
In order to finish marking with a non-empty free list, we will
|
||||
use 40% of the time for marking, and 60% for sweeping.
|
||||
|
||||
If TW is the total work for this cycle,
|
||||
MW = 40/100 * TW
|
||||
SW = 60/100 * TW
|
||||
Let MT be the time spent marking, ST the time spent sweeping, and TT
|
||||
the total time for this cycle. We have:
|
||||
MT = 40/100 * TT
|
||||
ST = 60/100 * TT
|
||||
|
||||
Amount of work to do for this slice:
|
||||
W = P * TW
|
||||
Amount of time to spend on this slice:
|
||||
T = P * TT = P * MT / (40/100) = P * ST / (60/100)
|
||||
|
||||
Since we must do MW work in MT time or SW work in ST time, the amount
|
||||
of work for this slice is:
|
||||
MS = P * MW / (40/100) if marking
|
||||
SS = P * SW / (60/100) if sweeping
|
||||
|
||||
Amount of marking work for a marking slice:
|
||||
MS = P * MW / (40/100)
|
||||
MS = P * caml_stat_heap_wsz * 250 / (100 + caml_percent_free)
|
||||
MS = P * (caml_stat_heap_wsz * 250 / (100 + caml_percent_free)
|
||||
+ 2.5 * caml_incremental_roots_count)
|
||||
Amount of sweeping work for a sweeping slice:
|
||||
SS = P * SW / (60/100)
|
||||
SS = P * caml_stat_heap_wsz * 5 / 3
|
||||
|
@ -421,8 +538,7 @@ intnat caml_major_collection_slice (intnat howmuch)
|
|||
*/
|
||||
|
||||
if (caml_major_slice_begin_hook != NULL) (*caml_major_slice_begin_hook) ();
|
||||
|
||||
if (caml_gc_phase == Phase_idle) start_cycle ();
|
||||
CAML_INSTR_SETUP (tmr, "major");
|
||||
|
||||
p = (double) caml_allocated_words * 3.0 * (100 + caml_percent_free)
|
||||
/ caml_stat_heap_wsz / caml_percent_free / 2.0;
|
||||
|
@ -434,51 +550,133 @@ intnat caml_major_collection_slice (intnat howmuch)
|
|||
}
|
||||
if (p < dp) p = dp;
|
||||
if (p < caml_extra_heap_resources) p = caml_extra_heap_resources;
|
||||
if (p > 0.3) p = 0.3;
|
||||
CAML_INSTR_INT ("major/work/extra#",
|
||||
(uintnat) (caml_extra_heap_resources * 1000000));
|
||||
|
||||
caml_gc_message (0x40, "ordered work = %ld words\n", howmuch);
|
||||
caml_gc_message (0x40, "allocated_words = %"
|
||||
ARCH_INTNAT_PRINTF_FORMAT "u\n",
|
||||
caml_allocated_words);
|
||||
caml_gc_message (0x40, "extra_heap_resources = %"
|
||||
ARCH_INTNAT_PRINTF_FORMAT "uu\n",
|
||||
(uintnat) (caml_extra_heap_resources * 1000000));
|
||||
caml_gc_message (0x40, "amount of work to do = %"
|
||||
ARCH_INTNAT_PRINTF_FORMAT "uu\n",
|
||||
(uintnat) (p * 1000000));
|
||||
caml_gc_message (0x40, "raw work-to-do = %"
|
||||
ARCH_INTNAT_PRINTF_FORMAT "du\n",
|
||||
(intnat) (p * 1000000));
|
||||
|
||||
for (i = 0; i < caml_major_window; i++){
|
||||
caml_major_ring[i] += p / caml_major_window;
|
||||
}
|
||||
|
||||
if (caml_gc_clock >= 1.0){
|
||||
caml_gc_clock -= 1.0;
|
||||
++caml_major_ring_index;
|
||||
if (caml_major_ring_index >= caml_major_window){
|
||||
caml_major_ring_index = 0;
|
||||
}
|
||||
}
|
||||
if (howmuch == -1){
|
||||
/* auto-triggered GC slice: spend work credit on the current bucket,
|
||||
then do the remaining work, if any */
|
||||
/* Note that the minor GC guarantees that the major slice is called in
|
||||
automatic mode (with [howmuch] = -1) at least once per clock tick.
|
||||
This means we never leave a non-empty bucket behind. */
|
||||
spend = fmin (caml_major_work_credit,
|
||||
caml_major_ring[caml_major_ring_index]);
|
||||
caml_major_work_credit -= spend;
|
||||
filt_p = caml_major_ring[caml_major_ring_index] - spend;
|
||||
caml_major_ring[caml_major_ring_index] = 0.0;
|
||||
}else{
|
||||
/* forced GC slice: do work and add it to the credit */
|
||||
if (howmuch == 0){
|
||||
/* automatic setting: size of next bucket
|
||||
we do not use the current bucket, as it may be empty */
|
||||
int i = caml_major_ring_index + 1;
|
||||
if (i >= caml_major_window) i = 0;
|
||||
filt_p = caml_major_ring[i];
|
||||
}else{
|
||||
/* manual setting */
|
||||
filt_p = (double) howmuch * 3.0 * (100 + caml_percent_free)
|
||||
/ caml_stat_heap_wsz / caml_percent_free / 2.0;
|
||||
}
|
||||
caml_major_work_credit += filt_p;
|
||||
}
|
||||
|
||||
p = filt_p;
|
||||
|
||||
caml_gc_message (0x40, "filtered work-to-do = %"
|
||||
ARCH_INTNAT_PRINTF_FORMAT "du\n",
|
||||
(intnat) (p * 1000000));
|
||||
|
||||
if (caml_gc_phase == Phase_idle){
|
||||
if (caml_young_ptr == caml_young_alloc_end){
|
||||
/* We can only start a major GC cycle if the minor allocation arena
|
||||
is empty, otherwise we'd have to treat it as a set of roots. */
|
||||
start_cycle ();
|
||||
CAML_INSTR_TIME (tmr, "major/roots");
|
||||
}
|
||||
p = 0;
|
||||
goto finished;
|
||||
}
|
||||
|
||||
if (p < 0){
|
||||
p = 0;
|
||||
goto finished;
|
||||
}
|
||||
|
||||
if (caml_gc_phase == Phase_mark){
|
||||
computed_work = (intnat) (p * caml_stat_heap_wsz * 250
|
||||
/ (100 + caml_percent_free));
|
||||
computed_work = (intnat) (p * (caml_stat_heap_wsz * 250
|
||||
/ (100 + caml_percent_free)
|
||||
+ caml_incremental_roots_count));
|
||||
}else{
|
||||
computed_work = (intnat) (p * caml_stat_heap_wsz * 5 / 3);
|
||||
}
|
||||
caml_gc_message (0x40, "ordered work = %ld words\n", howmuch);
|
||||
caml_gc_message (0x40, "computed work = %ld words\n", computed_work);
|
||||
if (howmuch == 0) howmuch = computed_work;
|
||||
if (caml_gc_phase == Phase_mark){
|
||||
mark_slice (howmuch);
|
||||
CAML_INSTR_INT ("major/work/mark#", computed_work);
|
||||
mark_slice (computed_work);
|
||||
CAML_INSTR_TIME (tmr, mark_slice_name[caml_gc_subphase]);
|
||||
caml_gc_message (0x02, "!", 0);
|
||||
}else{
|
||||
Assert (caml_gc_phase == Phase_sweep);
|
||||
sweep_slice (howmuch);
|
||||
CAML_INSTR_INT ("major/work/sweep#", computed_work);
|
||||
sweep_slice (computed_work);
|
||||
CAML_INSTR_TIME (tmr, "major/sweep");
|
||||
caml_gc_message (0x02, "$", 0);
|
||||
}
|
||||
|
||||
if (caml_gc_phase == Phase_idle) caml_compact_heap_maybe ();
|
||||
if (caml_gc_phase == Phase_idle){
|
||||
caml_compact_heap_maybe ();
|
||||
CAML_INSTR_TIME (tmr, "major/check_and_compact");
|
||||
}
|
||||
|
||||
finished:
|
||||
caml_gc_message (0x40, "work-done = %"
|
||||
ARCH_INTNAT_PRINTF_FORMAT "du\n",
|
||||
(intnat) (p * 1000000));
|
||||
|
||||
/* if some of the work was not done, take it back from the credit
|
||||
or spread it over the buckets. */
|
||||
p = filt_p - p;
|
||||
spend = fmin (p, caml_major_work_credit);
|
||||
caml_major_work_credit -= spend;
|
||||
if (p > spend){
|
||||
p -= spend;
|
||||
p /= caml_major_window;
|
||||
for (i = 0; i < caml_major_window; i++) caml_major_ring[i] += p;
|
||||
}
|
||||
|
||||
caml_stat_major_words += caml_allocated_words;
|
||||
caml_allocated_words = 0;
|
||||
caml_dependent_allocated = 0;
|
||||
caml_extra_heap_resources = 0.0;
|
||||
if (caml_major_slice_end_hook != NULL) (*caml_major_slice_end_hook) ();
|
||||
return computed_work;
|
||||
}
|
||||
|
||||
/* The minor heap must be empty when this function is called;
|
||||
the minor heap is empty when this function returns.
|
||||
*/
|
||||
/* This does not call caml_compact_heap_maybe because the estimations of
|
||||
/* This does not call [caml_compact_heap_maybe] because the estimates of
|
||||
free and live memory are only valid for a cycle done incrementally.
|
||||
Besides, this function is called by caml_compact_heap_maybe.
|
||||
Besides, this function itself is called by [caml_compact_heap_maybe].
|
||||
*/
|
||||
void caml_finish_major_cycle (void)
|
||||
{
|
||||
|
@ -491,27 +689,12 @@ void caml_finish_major_cycle (void)
|
|||
caml_allocated_words = 0;
|
||||
}
|
||||
|
||||
/* Make sure the request is at least Heap_chunk_min and round it up
|
||||
to a multiple of the page size.
|
||||
The argument and result are both numbers of words.
|
||||
/* Call this function to make sure [bsz] is greater than or equal
|
||||
to both [Heap_chunk_min] and the current heap increment.
|
||||
*/
|
||||
static asize_t clip_heap_chunk_size (asize_t request)
|
||||
asize_t caml_clip_heap_chunk_wsz (asize_t wsz)
|
||||
{
|
||||
if (request < Heap_chunk_min){
|
||||
request = Heap_chunk_min;
|
||||
}
|
||||
return
|
||||
Wsize_bsize (((Bsize_wsize (request) + Page_size - 1)
|
||||
>> Page_log) << Page_log);
|
||||
}
|
||||
|
||||
/* Compute the heap increment, make sure the request is at least that big,
|
||||
then call clip_heap_chunk_size, then make sure the result is >= request.
|
||||
The argument and result are both numbers of words.
|
||||
*/
|
||||
asize_t caml_round_heap_chunk_wsz (asize_t request)
|
||||
{
|
||||
asize_t result = request;
|
||||
asize_t result = wsz;
|
||||
uintnat incr;
|
||||
|
||||
/* Compute the heap increment as a word size. */
|
||||
|
@ -524,11 +707,8 @@ asize_t caml_round_heap_chunk_wsz (asize_t request)
|
|||
if (result < incr){
|
||||
result = incr;
|
||||
}
|
||||
result = clip_heap_chunk_size (result);
|
||||
|
||||
if (result < request){
|
||||
caml_raise_out_of_memory ();
|
||||
return 0; /* not reached */
|
||||
if (result < Heap_chunk_min){
|
||||
result = Heap_chunk_min;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -536,21 +716,25 @@ asize_t caml_round_heap_chunk_wsz (asize_t request)
|
|||
/* [heap_size] is a number of bytes */
|
||||
void caml_init_major_heap (asize_t heap_size)
|
||||
{
|
||||
caml_stat_heap_wsz = Wsize_bsize (clip_heap_chunk_size (heap_size));
|
||||
int i;
|
||||
|
||||
caml_stat_heap_wsz = caml_clip_heap_chunk_wsz (Wsize_bsize (heap_size));
|
||||
caml_stat_top_heap_wsz = caml_stat_heap_wsz;
|
||||
Assert (Bsize_wsize (caml_stat_heap_wsz) % Page_size == 0);
|
||||
caml_heap_start =
|
||||
(char *) caml_alloc_for_heap (Bsize_wsize (caml_stat_heap_wsz));
|
||||
if (caml_heap_start == NULL)
|
||||
caml_fatal_error ("Fatal error: not enough memory for the initial heap.\n");
|
||||
caml_fatal_error ("Fatal error: cannot allocate initial major heap.\n");
|
||||
Chunk_next (caml_heap_start) = NULL;
|
||||
caml_stat_heap_wsz = Wsize_bsize (Chunk_size (caml_heap_start));
|
||||
caml_stat_heap_chunks = 1;
|
||||
caml_stat_top_heap_wsz = caml_stat_heap_wsz;
|
||||
|
||||
if (caml_page_table_add(In_heap, caml_heap_start,
|
||||
caml_heap_start + Bsize_wsize (caml_stat_heap_wsz))
|
||||
!= 0) {
|
||||
caml_fatal_error ("Fatal error: not enough memory "
|
||||
"for the initial page table.\n");
|
||||
caml_fatal_error ("Fatal error: cannot allocate "
|
||||
"initial page table.\n");
|
||||
}
|
||||
|
||||
caml_fl_init_merge ();
|
||||
|
@ -566,4 +750,21 @@ void caml_init_major_heap (asize_t heap_size)
|
|||
heap_is_pure = 1;
|
||||
caml_allocated_words = 0;
|
||||
caml_extra_heap_resources = 0.0;
|
||||
for (i = 0; i < Max_major_window; i++) caml_major_ring[i] = 0.0;
|
||||
}
|
||||
|
||||
void caml_set_major_window (int w){
|
||||
uintnat total = 0;
|
||||
int i;
|
||||
if (w == caml_major_window) return;
|
||||
CAMLassert (w <= Max_major_window);
|
||||
/* Collect the current work-to-do from the buckets. */
|
||||
for (i = 0; i < caml_major_window; i++){
|
||||
total += caml_major_ring[i];
|
||||
}
|
||||
/* Redistribute to the new buckets. */
|
||||
for (i = 0; i < w; i++){
|
||||
caml_major_ring[i] = total / w;
|
||||
}
|
||||
caml_major_window = w;
|
||||
}
|
||||
|
|
114
byterun/memory.c
114
byterun/memory.c
|
@ -14,6 +14,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "caml/address_class.h"
|
||||
#include "caml/config.h"
|
||||
#include "caml/fail.h"
|
||||
#include "caml/freelist.h"
|
||||
#include "caml/gc.h"
|
||||
|
@ -26,9 +27,17 @@
|
|||
#include "caml/mlvalues.h"
|
||||
#include "caml/signals.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define inline _inline
|
||||
#endif
|
||||
int caml_huge_fallback_count = 0;
|
||||
/* Number of times that mmapping big pages fails and we fell back to small
|
||||
pages. This counter is available to the program through
|
||||
[Gc.huge_fallback_count].
|
||||
*/
|
||||
|
||||
uintnat caml_use_huge_pages = 0;
|
||||
/* True iff the program allocates heap chunks by mmapping huge pages.
|
||||
This is set when parsing [OCAMLRUNPARAM] and must stay constant
|
||||
after that.
|
||||
*/
|
||||
|
||||
extern uintnat caml_percent_free; /* major_gc.c */
|
||||
|
||||
|
@ -221,25 +230,56 @@ int caml_page_table_remove(int kind, void * start, void * end)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the [alloc_for_heap] system.
|
||||
This function must be called exactly once, and it must be called
|
||||
before the first call to [alloc_for_heap].
|
||||
It returns 0 on success and -1 on failure.
|
||||
*/
|
||||
int caml_init_alloc_for_heap (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate a block of the requested size, to be passed to
|
||||
[caml_add_to_heap] later.
|
||||
[request] must be a multiple of [Page_size], it is a number of bytes.
|
||||
[caml_alloc_for_heap] returns NULL if the request cannot be satisfied.
|
||||
The returned pointer is a hp, but the header must be initialized by
|
||||
the caller.
|
||||
[request] will be rounded up to some implementation-dependent size.
|
||||
The caller must use [Chunk_size] on the result to recover the actual
|
||||
size.
|
||||
Return NULL if the request cannot be satisfied. The returned pointer
|
||||
is a hp, but the header (and the contents) must be initialized by the
|
||||
caller.
|
||||
*/
|
||||
char *caml_alloc_for_heap (asize_t request)
|
||||
{
|
||||
char *mem;
|
||||
void *block;
|
||||
Assert (request % Page_size == 0);
|
||||
mem = caml_aligned_malloc (request + sizeof (heap_chunk_head),
|
||||
sizeof (heap_chunk_head), &block);
|
||||
if (mem == NULL) return NULL;
|
||||
mem += sizeof (heap_chunk_head);
|
||||
Chunk_size (mem) = request;
|
||||
Chunk_block (mem) = block;
|
||||
return mem;
|
||||
if (caml_use_huge_pages){
|
||||
#ifdef HAS_HUGE_PAGES
|
||||
uintnat size = Round_mmap_size (sizeof (heap_chunk_head) + request);
|
||||
void *block;
|
||||
char *mem;
|
||||
block = mmap (NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
|
||||
if (block == MAP_FAILED) return NULL;
|
||||
mem = (char *) block + sizeof (heap_chunk_head);
|
||||
Chunk_size (mem) = size - sizeof (heap_chunk_head);
|
||||
Chunk_block (mem) = block;
|
||||
return mem;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}else{
|
||||
char *mem;
|
||||
void *block;
|
||||
|
||||
request = ((request + Page_size - 1) >> Page_log) << Page_log;
|
||||
mem = caml_aligned_malloc (request + sizeof (heap_chunk_head),
|
||||
sizeof (heap_chunk_head), &block);
|
||||
if (mem == NULL) return NULL;
|
||||
mem += sizeof (heap_chunk_head);
|
||||
Chunk_size (mem) = request;
|
||||
Chunk_block (mem) = block;
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use this function to free a block allocated with [caml_alloc_for_heap]
|
||||
|
@ -247,7 +287,15 @@ char *caml_alloc_for_heap (asize_t request)
|
|||
*/
|
||||
void caml_free_for_heap (char *mem)
|
||||
{
|
||||
free (Chunk_block (mem));
|
||||
if (caml_use_huge_pages){
|
||||
#ifdef HAS_HUGE_PAGES
|
||||
munmap (Chunk_block (mem), Chunk_size (mem) + sizeof (heap_chunk_head));
|
||||
#else
|
||||
CAMLassert (0);
|
||||
#endif
|
||||
}else{
|
||||
free (Chunk_block (mem));
|
||||
}
|
||||
}
|
||||
|
||||
/* Take a chunk of memory as argument, which must be the result of a
|
||||
|
@ -263,10 +311,9 @@ void caml_free_for_heap (char *mem)
|
|||
*/
|
||||
int caml_add_to_heap (char *m)
|
||||
{
|
||||
Assert (Chunk_size (m) % Page_size == 0);
|
||||
#ifdef DEBUG
|
||||
/* Should check the contents of the block. */
|
||||
#endif /* debug */
|
||||
#endif /* DEBUG */
|
||||
|
||||
caml_gc_message (0x04, "Growing heap to %luk bytes\n",
|
||||
(Bsize_wsize (caml_stat_heap_wsz) + Chunk_size (m)) / 1024);
|
||||
|
@ -314,14 +361,14 @@ static value *expand_heap (mlsize_t request)
|
|||
asize_t over_request, malloc_request, remain;
|
||||
|
||||
Assert (request <= Max_wosize);
|
||||
over_request = Whsize_wosize (request + request / 100 * caml_percent_free);
|
||||
malloc_request = caml_round_heap_chunk_wsz (over_request);
|
||||
over_request = request + request / 100 * caml_percent_free;
|
||||
malloc_request = caml_clip_heap_chunk_wsz (over_request);
|
||||
mem = (value *) caml_alloc_for_heap (Bsize_wsize (malloc_request));
|
||||
if (mem == NULL){
|
||||
caml_gc_message (0x04, "No room for growing heap\n", 0);
|
||||
return NULL;
|
||||
}
|
||||
remain = malloc_request;
|
||||
remain = Wsize_bsize (Chunk_size (mem));
|
||||
prev = hp = mem;
|
||||
/* FIXME find a way to do this with a call to caml_make_free_blocks */
|
||||
while (Wosize_whsize (remain) > Max_wosize){
|
||||
|
@ -451,7 +498,8 @@ static inline value caml_alloc_shr_aux (mlsize_t wosize, tag_t tag,
|
|||
Assert (Hd_hp (hp) == Make_header (wosize, tag, caml_allocation_color (hp)));
|
||||
caml_allocated_words += Whsize_wosize (wosize);
|
||||
if (caml_allocated_words > caml_minor_heap_wsz){
|
||||
caml_urge_major_slice ();
|
||||
CAML_INSTR_INT ("request_major/alloc_shr@", 1);
|
||||
caml_request_major_slice ();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
{
|
||||
|
@ -512,13 +560,15 @@ CAMLexport void caml_adjust_gc_speed (mlsize_t res, mlsize_t max)
|
|||
if (res > max) res = max;
|
||||
caml_extra_heap_resources += (double) res / (double) max;
|
||||
if (caml_extra_heap_resources > 1.0){
|
||||
CAML_INSTR_INT ("request_major/adjust_gc_speed_1@", 1);
|
||||
caml_extra_heap_resources = 1.0;
|
||||
caml_urge_major_slice ();
|
||||
caml_request_major_slice ();
|
||||
}
|
||||
if (caml_extra_heap_resources
|
||||
> (double) caml_minor_heap_wsz / 2.0
|
||||
/ (double) caml_stat_heap_wsz) {
|
||||
caml_urge_major_slice ();
|
||||
CAML_INSTR_INT ("request_major/adjust_gc_speed_2@", 1);
|
||||
caml_request_major_slice ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,10 +585,7 @@ CAMLexport CAMLweakdef void caml_initialize (value *fp, value val)
|
|||
CAMLassert(Is_in_heap(fp));
|
||||
*fp = val;
|
||||
if (Is_block (val) && Is_young (val)) {
|
||||
if (caml_ref_table.ptr >= caml_ref_table.limit){
|
||||
caml_realloc_ref_table (&caml_ref_table);
|
||||
}
|
||||
*caml_ref_table.ptr++ = fp;
|
||||
add_to_ref_table (&caml_ref_table, fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,12 +633,7 @@ CAMLexport CAMLweakdef void caml_modify (value *fp, value val)
|
|||
}
|
||||
/* Check for condition 1. */
|
||||
if (Is_block(val) && Is_young(val)) {
|
||||
/* Add [fp] to remembered set */
|
||||
if (caml_ref_table.ptr >= caml_ref_table.limit){
|
||||
CAMLassert (caml_ref_table.ptr == caml_ref_table.limit);
|
||||
caml_realloc_ref_table (&caml_ref_table);
|
||||
}
|
||||
*caml_ref_table.ptr++ = fp;
|
||||
add_to_ref_table (&caml_ref_table, fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,36 @@
|
|||
#include "caml/signals.h"
|
||||
#include "caml/weak.h"
|
||||
|
||||
/* Pointers into the minor heap.
|
||||
[caml_young_base]
|
||||
The [malloc] block that contains the heap.
|
||||
[caml_young_start] ... [caml_young_end]
|
||||
The whole range of the minor heap: all young blocks are inside
|
||||
this interval.
|
||||
[caml_young_alloc_start]...[caml_young_alloc_end]
|
||||
The allocation arena: newly-allocated blocks are carved from
|
||||
this interval.
|
||||
[caml_young_alloc_mid] is the mid-point of this interval.
|
||||
[caml_young_ptr], [caml_young_trigger], [caml_young_limit]
|
||||
These pointers are all inside the allocation arena.
|
||||
- [caml_young_ptr] is where the next allocation will take place.
|
||||
- [caml_young_trigger] is how far we can allocate before triggering
|
||||
[caml_gc_dispatch]. Currently, it is either [caml_young_alloc_start]
|
||||
or the mid-point of the allocation arena.
|
||||
- [caml_young_limit] is the pointer that is compared to
|
||||
[caml_young_ptr] for allocation. It is either
|
||||
[caml_young_alloc_end] if a signal is pending and we are in
|
||||
native code, or [caml_young_trigger].
|
||||
*/
|
||||
|
||||
asize_t caml_minor_heap_wsz;
|
||||
static void *caml_young_base = NULL;
|
||||
CAMLexport value *caml_young_start = NULL, *caml_young_end = NULL;
|
||||
CAMLexport value *caml_young_alloc_start = NULL,
|
||||
*caml_young_alloc_mid = NULL,
|
||||
*caml_young_alloc_end = NULL;
|
||||
CAMLexport value *caml_young_ptr = NULL, *caml_young_limit = NULL;
|
||||
CAMLexport value *caml_young_trigger = NULL;
|
||||
|
||||
CAMLexport struct caml_ref_table
|
||||
caml_ref_table = { NULL, NULL, NULL, NULL, NULL, 0, 0},
|
||||
|
@ -40,10 +66,6 @@ CAMLexport struct caml_ref_table
|
|||
|
||||
int caml_in_minor_collection = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
static unsigned long minor_gc_counter = 0;
|
||||
#endif
|
||||
|
||||
/* [sz] and [rsv] are numbers of entries */
|
||||
void caml_alloc_table (struct caml_ref_table *tbl, asize_t sz, asize_t rsv)
|
||||
{
|
||||
|
@ -75,32 +97,73 @@ static void clear_table (struct caml_ref_table *tbl)
|
|||
tbl->limit = tbl->threshold;
|
||||
}
|
||||
|
||||
/* [size] is a number of bytes */
|
||||
void caml_set_minor_heap_size (asize_t size)
|
||||
void caml_set_minor_heap_size (asize_t bsz)
|
||||
{
|
||||
char *new_heap;
|
||||
void *new_heap_base;
|
||||
|
||||
Assert (size >= Bsize_wsize(Minor_heap_min));
|
||||
Assert (size <= Bsize_wsize(Minor_heap_max));
|
||||
Assert (size % sizeof (value) == 0);
|
||||
if (caml_young_ptr != caml_young_end) caml_minor_collection ();
|
||||
Assert (caml_young_ptr == caml_young_end);
|
||||
new_heap = caml_aligned_malloc(size, 0, &new_heap_base);
|
||||
Assert (bsz >= Bsize_wsize(Minor_heap_min));
|
||||
Assert (bsz <= Bsize_wsize(Minor_heap_max));
|
||||
Assert (bsz % sizeof (value) == 0);
|
||||
if (caml_young_ptr != caml_young_alloc_end){
|
||||
CAML_INSTR_INT ("force_minor/set_minor_heap_size@", 1);
|
||||
caml_requested_minor_gc = 0;
|
||||
caml_young_trigger = caml_young_alloc_mid;
|
||||
caml_young_limit = caml_young_trigger;
|
||||
caml_empty_minor_heap ();
|
||||
}
|
||||
CAMLassert (caml_young_ptr == caml_young_alloc_end);
|
||||
#ifdef MMAP_INTERVAL
|
||||
{
|
||||
static uintnat minor_heap_mapped_bsz = 0;
|
||||
uintnat new_mapped_bsz;
|
||||
new_mapped_bsz = Round_mmap_size (bsz);
|
||||
void *block;
|
||||
|
||||
CAMLassert (caml_young_start != NULL);
|
||||
if (new_mapped_bsz > minor_heap_mapped_bsz){
|
||||
uintnat addbsz = new_mapped_bsz - minor_heap_mapped_bsz;
|
||||
new_heap = (char *) caml_young_start - addbsz;
|
||||
block = caml_mmap_heap (new_heap, addbsz, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED);
|
||||
if (block != new_heap){
|
||||
if (minor_heap_mapped_bsz == 0){
|
||||
caml_fatal_error ("cannot initialize minor heap: mmap failed\n");
|
||||
}else{
|
||||
caml_raise_out_of_memory ();
|
||||
}
|
||||
}
|
||||
new_heap_base = new_heap;
|
||||
}else if (new_mapped_bsz < minor_heap_mapped_bsz){
|
||||
uintnat subbsz = minor_heap_mapped_bsz - new_mapped_bsz;
|
||||
(void) caml_mmap_heap (caml_young_start, subbsz, PROT_NONE,
|
||||
MAP_FIXED | MAP_NORESERVE);
|
||||
new_heap_base = new_heap = (char *) caml_young_start + subbsz;
|
||||
}else{
|
||||
new_heap_base = new_heap = caml_young_base;
|
||||
}
|
||||
}
|
||||
#else
|
||||
new_heap = caml_aligned_malloc(bsz, 0, &new_heap_base);
|
||||
if (new_heap == NULL) caml_raise_out_of_memory();
|
||||
if (caml_page_table_add(In_young, new_heap, new_heap + size) != 0)
|
||||
if (caml_page_table_add(In_young, new_heap, new_heap + bsz) != 0)
|
||||
caml_raise_out_of_memory();
|
||||
|
||||
if (caml_young_start != NULL){
|
||||
caml_page_table_remove(In_young, caml_young_start, caml_young_end);
|
||||
free (caml_young_base);
|
||||
}
|
||||
#endif
|
||||
caml_young_base = new_heap_base;
|
||||
caml_young_start = (value *) new_heap;
|
||||
caml_young_end = (value *) (new_heap + size);
|
||||
caml_young_limit = caml_young_start;
|
||||
caml_young_ptr = caml_young_end;
|
||||
caml_minor_heap_wsz = Wsize_bsize (size);
|
||||
caml_young_end = (value *) (new_heap + bsz);
|
||||
caml_young_alloc_start = caml_young_start;
|
||||
caml_young_alloc_mid = caml_young_alloc_start + Wsize_bsize (bsz) / 2;
|
||||
caml_young_alloc_end = caml_young_end;
|
||||
caml_young_trigger = caml_young_alloc_start;
|
||||
caml_young_limit = caml_young_trigger;
|
||||
caml_young_ptr = caml_young_alloc_end;
|
||||
caml_minor_heap_wsz = Wsize_bsize (bsz);
|
||||
|
||||
reset_table (&caml_ref_table);
|
||||
reset_table (&caml_weak_ref_table);
|
||||
|
@ -232,16 +295,20 @@ void caml_empty_minor_heap (void)
|
|||
value **r;
|
||||
uintnat prev_alloc_words;
|
||||
|
||||
if (caml_young_ptr != caml_young_end){
|
||||
if (caml_young_ptr != caml_young_alloc_end){
|
||||
if (caml_minor_gc_begin_hook != NULL) (*caml_minor_gc_begin_hook) ();
|
||||
CAML_INSTR_SETUP (tmr, "minor");
|
||||
prev_alloc_words = caml_allocated_words;
|
||||
caml_in_minor_collection = 1;
|
||||
caml_gc_message (0x02, "<", 0);
|
||||
caml_oldify_local_roots();
|
||||
CAML_INSTR_TIME (tmr, "minor/local_roots");
|
||||
for (r = caml_ref_table.base; r < caml_ref_table.ptr; r++){
|
||||
caml_oldify_one (**r, *r);
|
||||
}
|
||||
CAML_INSTR_TIME (tmr, "minor/ref_table");
|
||||
caml_oldify_mopup ();
|
||||
CAML_INSTR_TIME (tmr, "minor/copy");
|
||||
for (r = caml_weak_ref_table.base; r < caml_weak_ref_table.ptr; r++){
|
||||
if (Is_block (**r) && Is_young (**r)){
|
||||
if (Hd_val (**r) == 0){
|
||||
|
@ -258,18 +325,22 @@ void caml_empty_minor_heap (void)
|
|||
final_fun((value)*r);
|
||||
}
|
||||
}
|
||||
if (caml_young_ptr < caml_young_start) caml_young_ptr = caml_young_start;
|
||||
caml_stat_minor_words += caml_young_end - caml_young_ptr;
|
||||
caml_young_ptr = caml_young_end;
|
||||
caml_young_limit = caml_young_start;
|
||||
CAML_INSTR_TIME (tmr, "minor/update_weak");
|
||||
CAMLassert (caml_young_ptr >= caml_young_alloc_start);
|
||||
caml_stat_minor_words += caml_young_alloc_end - caml_young_ptr;
|
||||
caml_gc_clock += (double) (caml_young_alloc_end - caml_young_ptr)
|
||||
/ caml_minor_heap_wsz;
|
||||
caml_young_ptr = caml_young_alloc_end;
|
||||
clear_table (&caml_ref_table);
|
||||
clear_table (&caml_weak_ref_table);
|
||||
clear_table (&caml_finalize_table);
|
||||
caml_gc_message (0x02, ">", 0);
|
||||
caml_in_minor_collection = 0;
|
||||
caml_stat_promoted_words += caml_allocated_words - prev_alloc_words;
|
||||
++ caml_stat_minor_collections;
|
||||
caml_final_empty_young ();
|
||||
CAML_INSTR_TIME (tmr, "minor/finalized");
|
||||
caml_stat_promoted_words += caml_allocated_words - prev_alloc_words;
|
||||
CAML_INSTR_INT ("minor/promoted#", caml_allocated_words - prev_alloc_words);
|
||||
++ caml_stat_minor_collections;
|
||||
if (caml_minor_gc_end_hook != NULL) (*caml_minor_gc_end_hook) ();
|
||||
}else{
|
||||
caml_final_empty_young ();
|
||||
|
@ -277,34 +348,83 @@ void caml_empty_minor_heap (void)
|
|||
#ifdef DEBUG
|
||||
{
|
||||
value *p;
|
||||
for (p = caml_young_start; p < caml_young_end; ++p){
|
||||
for (p = caml_young_alloc_start; p < caml_young_alloc_end; ++p){
|
||||
*p = Debug_free_minor;
|
||||
}
|
||||
++ minor_gc_counter;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Do a minor collection and a slice of major collection, call finalisation
|
||||
#ifdef CAML_INSTR
|
||||
extern uintnat caml_instr_alloc_jump;
|
||||
#endif
|
||||
|
||||
/* Do a minor collection or a slice of major collection, call finalisation
|
||||
functions, etc.
|
||||
Leave the minor heap empty.
|
||||
Leave enough room in the minor heap to allocate at least one object.
|
||||
*/
|
||||
CAMLexport void caml_gc_dispatch (void)
|
||||
{
|
||||
value *trigger = caml_young_trigger; /* save old value of trigger */
|
||||
#ifdef CAML_INSTR
|
||||
CAML_INSTR_SETUP(tmr, "dispatch");
|
||||
CAML_INSTR_TIME (tmr, "overhead");
|
||||
CAML_INSTR_INT ("alloc/jump#", caml_instr_alloc_jump);
|
||||
caml_instr_alloc_jump = 0;
|
||||
#endif
|
||||
|
||||
if (trigger == caml_young_alloc_start || caml_requested_minor_gc){
|
||||
/* The minor heap is full, we must do a minor collection. */
|
||||
/* reset the pointers first because the end hooks might allocate */
|
||||
caml_requested_minor_gc = 0;
|
||||
caml_young_trigger = caml_young_alloc_mid;
|
||||
caml_young_limit = caml_young_trigger;
|
||||
caml_empty_minor_heap ();
|
||||
/* The minor heap is empty, we can start a major collection. */
|
||||
if (caml_gc_phase == Phase_idle) caml_major_collection_slice (-1);
|
||||
CAML_INSTR_TIME (tmr, "dispatch/minor");
|
||||
|
||||
caml_final_do_calls ();
|
||||
CAML_INSTR_TIME (tmr, "dispatch/finalizers");
|
||||
|
||||
while (caml_young_ptr - caml_young_alloc_start < Max_young_whsize){
|
||||
/* The finalizers or the hooks have filled up the minor heap, we must
|
||||
repeat the minor collection. */
|
||||
caml_requested_minor_gc = 0;
|
||||
caml_young_trigger = caml_young_alloc_mid;
|
||||
caml_young_limit = caml_young_trigger;
|
||||
caml_empty_minor_heap ();
|
||||
/* The minor heap is empty, we can start a major collection. */
|
||||
if (caml_gc_phase == Phase_idle) caml_major_collection_slice (-1);
|
||||
CAML_INSTR_TIME (tmr, "dispatch/finalizers_minor");
|
||||
}
|
||||
}
|
||||
if (trigger != caml_young_alloc_start || caml_requested_major_slice){
|
||||
/* The minor heap is half-full, do a major GC slice. */
|
||||
caml_requested_major_slice = 0;
|
||||
caml_young_trigger = caml_young_alloc_start;
|
||||
caml_young_limit = caml_young_trigger;
|
||||
caml_major_collection_slice (-1);
|
||||
CAML_INSTR_TIME (tmr, "dispatch/major");
|
||||
}
|
||||
}
|
||||
|
||||
/* For backward compatibility with Lablgtk: do a minor collection to
|
||||
ensure that the minor heap is empty.
|
||||
*/
|
||||
CAMLexport void caml_minor_collection (void)
|
||||
{
|
||||
caml_empty_minor_heap ();
|
||||
|
||||
caml_major_collection_slice (0);
|
||||
caml_force_major_slice = 0;
|
||||
|
||||
caml_final_do_calls ();
|
||||
|
||||
caml_empty_minor_heap ();
|
||||
caml_requested_minor_gc = 1;
|
||||
caml_gc_dispatch ();
|
||||
}
|
||||
|
||||
CAMLexport value caml_check_urgent_gc (value extra_root)
|
||||
{
|
||||
CAMLparam1 (extra_root);
|
||||
if (caml_force_major_slice) caml_minor_collection();
|
||||
if (caml_requested_major_slice || caml_requested_minor_gc){
|
||||
CAML_INSTR_INT ("force_minor/check_urgent_gc@", 1);
|
||||
caml_gc_dispatch();
|
||||
}
|
||||
CAMLreturn (extra_root);
|
||||
}
|
||||
|
||||
|
@ -316,13 +436,14 @@ void caml_realloc_ref_table (struct caml_ref_table *tbl)
|
|||
if (tbl->base == NULL){
|
||||
caml_alloc_table (tbl, caml_minor_heap_wsz / 8, 256);
|
||||
}else if (tbl->limit == tbl->threshold){
|
||||
CAML_INSTR_INT ("request_minor/realloc_ref_table@", 1);
|
||||
caml_gc_message (0x08, "ref_table threshold crossed\n", 0);
|
||||
tbl->limit = tbl->end;
|
||||
caml_urge_major_slice ();
|
||||
}else{ /* This will almost never happen with the bytecode interpreter. */
|
||||
caml_request_minor_gc ();
|
||||
}else{
|
||||
asize_t sz;
|
||||
asize_t cur_ptr = tbl->ptr - tbl->base;
|
||||
Assert (caml_force_major_slice);
|
||||
CAMLassert (caml_requested_minor_gc);
|
||||
|
||||
tbl->size *= 2;
|
||||
sz = (tbl->size + tbl->reserve) * sizeof (value *);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "caml/config.h"
|
||||
#include "caml/misc.h"
|
||||
#include "caml/memory.h"
|
||||
#include "caml/version.h"
|
||||
|
||||
caml_timing_hook caml_major_slice_begin_hook = NULL;
|
||||
caml_timing_hook caml_major_slice_end_hook = NULL;
|
||||
|
@ -49,7 +50,7 @@ uintnat caml_verb_gc = 0;
|
|||
|
||||
void caml_gc_message (int level, char *msg, uintnat arg)
|
||||
{
|
||||
if (level < 0 || (caml_verb_gc & level) != 0){
|
||||
if ((caml_verb_gc & level) != 0){
|
||||
fprintf (stderr, msg, arg);
|
||||
fflush (stderr);
|
||||
}
|
||||
|
@ -195,3 +196,81 @@ int caml_runtime_warnings_active(void)
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CAML_INSTR
|
||||
/* Timers for profiling GC and allocation (experimental, Linux-only) */
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct CAML_INSTR_BLOCK *CAML_INSTR_LOG = NULL;
|
||||
intnat CAML_INSTR_STARTTIME, CAML_INSTR_STOPTIME;
|
||||
|
||||
#define Get_time(p,i) ((p)->ts[(i)].tv_nsec + 1000000000 * (p)->ts[(i)].tv_sec)
|
||||
|
||||
void CAML_INSTR_INIT (void)
|
||||
{
|
||||
char *s;
|
||||
|
||||
CAML_INSTR_STARTTIME = 0;
|
||||
s = getenv ("OCAML_INSTR_START");
|
||||
if (s != NULL) CAML_INSTR_STARTTIME = atol (s);
|
||||
CAML_INSTR_STOPTIME = LONG_MAX;
|
||||
s = getenv ("OCAML_INSTR_STOP");
|
||||
if (s != NULL) CAML_INSTR_STOPTIME = atol (s);
|
||||
}
|
||||
|
||||
void CAML_INSTR_ATEXIT (void)
|
||||
{
|
||||
int i;
|
||||
struct CAML_INSTR_BLOCK *p, *prev, *next;
|
||||
FILE *f = NULL;
|
||||
char *fname;
|
||||
|
||||
fname = getenv ("OCAML_INSTR_FILE");
|
||||
if (fname != NULL){
|
||||
char *mode = "a";
|
||||
char buf [1000];
|
||||
char *name = fname;
|
||||
|
||||
if (name[0] == '@'){
|
||||
snprintf (buf, sizeof(buf), "%s.%d", name + 1, getpid ());
|
||||
name = buf;
|
||||
}
|
||||
if (name[0] == '+'){
|
||||
mode = "a";
|
||||
name = name + 1;
|
||||
}else if (name [0] == '>' || name[0] == '-'){
|
||||
mode = "w";
|
||||
name = name + 1;
|
||||
}
|
||||
f = fopen (name, mode);
|
||||
}
|
||||
|
||||
if (f != NULL){
|
||||
/* reverse the list */
|
||||
prev = NULL;
|
||||
p = CAML_INSTR_LOG;
|
||||
while (p != NULL){
|
||||
next = p->next;
|
||||
p->next = prev;
|
||||
prev = p;
|
||||
p = next;
|
||||
}
|
||||
CAML_INSTR_LOG = prev;
|
||||
fprintf (f, "==== OCAML INSTRUMENTATION DATA %s\n", OCAML_VERSION_STRING);
|
||||
for (p = CAML_INSTR_LOG; p != NULL; p = p->next){
|
||||
for (i = 0; i < p->index; i++){
|
||||
fprintf (f, "@@ %19ld %19ld %s\n",
|
||||
Get_time (p, i), Get_time(p, i+1), p->tag[i+1]);
|
||||
}
|
||||
if (p->tag[0][0] != '\000'){
|
||||
fprintf (f, "@@ %19ld %19ld %s\n",
|
||||
Get_time (p, 0), Get_time(p, p->index), p->tag[0]);
|
||||
}
|
||||
}
|
||||
fclose (f);
|
||||
}
|
||||
}
|
||||
#endif /* CAML_INSTR */
|
||||
|
|
|
@ -111,9 +111,13 @@ CAMLprim value caml_obj_dup(value arg)
|
|||
to 0 or greater than the current size.
|
||||
|
||||
algorithm:
|
||||
Change the length field of the header. Make up a white object
|
||||
Change the length field of the header. Make up a black object
|
||||
with the leftover part of the object: this is needed in the major
|
||||
heap and harmless in the minor heap.
|
||||
heap and harmless in the minor heap. The object cannot be white
|
||||
because there may still be references to it in the ref table. By
|
||||
using a black object we ensure that the ref table will be emptied
|
||||
before the block is reallocated (since there must be a minor
|
||||
collection within each major cycle).
|
||||
|
||||
[newsize] is a value encoding a number of words.
|
||||
*/
|
||||
|
@ -147,7 +151,7 @@ CAMLprim value caml_obj_truncate (value v, value newsize)
|
|||
look like a pointer because there may be some references to it in
|
||||
ref_table. */
|
||||
Field (v, new_wosize) =
|
||||
Make_header (Wosize_whsize (wosize-new_wosize), 1, Caml_white);
|
||||
Make_header (Wosize_whsize (wosize-new_wosize), Abstract_tag, Caml_black);
|
||||
Hd_val (v) = Make_header (new_wosize, tag, color);
|
||||
return Val_unit;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ CAMLexport struct caml__roots_block *caml_local_roots = NULL;
|
|||
|
||||
CAMLexport void (*caml_scan_roots_hook) (scanning_action f) = NULL;
|
||||
|
||||
/* FIXME should rename to [caml_oldify_young_roots] and synchronise with
|
||||
/* FIXME should rename to [caml_oldify_minor_roots] and synchronise with
|
||||
asmrun/roots.c */
|
||||
/* Call [caml_oldify_one] on (at least) all the roots that point to the minor
|
||||
heap. */
|
||||
|
@ -60,23 +60,38 @@ void caml_oldify_local_roots (void)
|
|||
|
||||
/* Call [caml_darken] on all roots */
|
||||
|
||||
void caml_darken_all_roots (void)
|
||||
void caml_darken_all_roots_start (void)
|
||||
{
|
||||
caml_do_roots (caml_darken);
|
||||
caml_do_roots (caml_darken, 1);
|
||||
}
|
||||
|
||||
void caml_do_roots (scanning_action f)
|
||||
uintnat caml_incremental_roots_count = 1;
|
||||
|
||||
intnat caml_darken_all_roots_slice (intnat work)
|
||||
{
|
||||
return work;
|
||||
}
|
||||
|
||||
/* Note, in byte-code there is only one global root, so [do_globals] is
|
||||
ignored and [caml_darken_all_roots_slice] does nothing. */
|
||||
void caml_do_roots (scanning_action f, int do_globals)
|
||||
{
|
||||
CAML_INSTR_SETUP (tmr, "major_roots");
|
||||
/* Global variables */
|
||||
f(caml_global_data, &caml_global_data);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/global");
|
||||
/* The stack and the local C roots */
|
||||
caml_do_local_roots(f, caml_extern_sp, caml_stack_high, caml_local_roots);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/local");
|
||||
/* Global C roots */
|
||||
caml_scan_global_roots(f);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/C");
|
||||
/* Finalised values */
|
||||
caml_final_do_strong_roots (f);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/finalised");
|
||||
/* Hook */
|
||||
if (caml_scan_roots_hook != NULL) (*caml_scan_roots_hook)(f);
|
||||
CAML_INSTR_TIME (tmr, "major_roots/hook");
|
||||
}
|
||||
|
||||
CAMLexport void caml_do_local_roots (scanning_action f, value *stack_low,
|
||||
|
|
|
@ -67,7 +67,7 @@ void caml_record_signal(int signal_number)
|
|||
#ifndef NATIVE_CODE
|
||||
caml_something_to_do = 1;
|
||||
#else
|
||||
caml_young_limit = caml_young_end;
|
||||
caml_young_limit = caml_young_alloc_end;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -157,15 +157,16 @@ void caml_execute_signal(int signal_number, int in_signal_handler)
|
|||
|
||||
/* Arrange for a garbage collection to be performed as soon as possible */
|
||||
|
||||
int volatile caml_force_major_slice = 0;
|
||||
int volatile caml_requested_major_slice = 0;
|
||||
int volatile caml_requested_minor_gc = 0;
|
||||
|
||||
void caml_urge_major_slice (void)
|
||||
void caml_request_major_slice (void)
|
||||
{
|
||||
caml_force_major_slice = 1;
|
||||
caml_requested_major_slice = 1;
|
||||
#ifndef NATIVE_CODE
|
||||
caml_something_to_do = 1;
|
||||
#else
|
||||
caml_young_limit = caml_young_end;
|
||||
caml_young_limit = caml_young_alloc_end;
|
||||
/* This is only moderately effective on ports that cache [caml_young_limit]
|
||||
in a register, since [caml_modify] is called directly, not through
|
||||
[caml_c_call], so it may take a while before the register is reloaded
|
||||
|
@ -173,6 +174,17 @@ void caml_urge_major_slice (void)
|
|||
#endif
|
||||
}
|
||||
|
||||
void caml_request_minor_gc (void)
|
||||
{
|
||||
caml_requested_minor_gc = 1;
|
||||
#ifndef NATIVE_CODE
|
||||
caml_something_to_do = 1;
|
||||
#else
|
||||
caml_young_limit = caml_young_alloc_end;
|
||||
/* Same remark as above in [caml_request_major_slice]. */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* OS-independent numbering of signals */
|
||||
|
||||
#ifndef SIGABRT
|
||||
|
|
|
@ -287,9 +287,13 @@ CAMLexport void caml_main(char **argv)
|
|||
caml_external_raise = NULL;
|
||||
/* Determine options and position of bytecode file */
|
||||
#ifdef DEBUG
|
||||
caml_verb_gc = 0xBF;
|
||||
caml_verb_gc = 0x3F;
|
||||
#endif
|
||||
caml_parse_ocamlrunparam();
|
||||
#ifdef DEBUG
|
||||
caml_gc_message (-1, "### OCaml runtime: debug mode ###\n", 0);
|
||||
#endif
|
||||
|
||||
pos = 0;
|
||||
|
||||
/* First, try argv[0] (when ocamlrun is called by a bytecode program) */
|
||||
|
@ -326,7 +330,7 @@ CAMLexport void caml_main(char **argv)
|
|||
/* Initialize the abstract machine */
|
||||
caml_init_gc (caml_init_minor_heap_wsz, caml_init_heap_wsz,
|
||||
caml_init_heap_chunk_sz, caml_init_percent_free,
|
||||
caml_init_max_percent_free);
|
||||
caml_init_max_percent_free, caml_init_major_window);
|
||||
caml_init_stack (caml_init_max_stack_wsz);
|
||||
caml_init_atom_table();
|
||||
caml_init_backtrace();
|
||||
|
@ -410,7 +414,7 @@ CAMLexport void caml_startup_code(
|
|||
/* Initialize the abstract machine */
|
||||
caml_init_gc (caml_init_minor_heap_wsz, caml_init_heap_wsz,
|
||||
caml_init_heap_chunk_sz, caml_init_percent_free,
|
||||
caml_init_max_percent_free);
|
||||
caml_init_max_percent_free, caml_init_major_window);
|
||||
caml_init_stack (caml_init_max_stack_wsz);
|
||||
caml_init_atom_table();
|
||||
caml_init_backtrace();
|
||||
|
|
|
@ -42,6 +42,7 @@ uintnat caml_init_minor_heap_wsz = Minor_heap_def;
|
|||
uintnat caml_init_heap_chunk_sz = Heap_chunk_def;
|
||||
uintnat caml_init_heap_wsz = Init_heap_def;
|
||||
uintnat caml_init_max_stack_wsz = Max_stack_def;
|
||||
uintnat caml_init_major_window = Major_window_def;
|
||||
extern int caml_parser_trace;
|
||||
uintnat caml_trace_level = 0;
|
||||
|
||||
|
@ -73,6 +74,7 @@ void caml_parse_ocamlrunparam(void)
|
|||
case 'a': scanmult (opt, &p); caml_set_allocation_policy (p); break;
|
||||
case 'b': scanmult (opt, &p); caml_record_backtrace(Val_bool (p)); break;
|
||||
case 'h': scanmult (opt, &caml_init_heap_wsz); break;
|
||||
case 'H': scanmult (opt, &caml_use_huge_pages); break;
|
||||
case 'i': scanmult (opt, &caml_init_heap_chunk_sz); break;
|
||||
case 'l': scanmult (opt, &caml_init_max_stack_wsz); break;
|
||||
case 'o': scanmult (opt, &caml_init_percent_free); break;
|
||||
|
@ -82,6 +84,7 @@ void caml_parse_ocamlrunparam(void)
|
|||
case 's': scanmult (opt, &caml_init_minor_heap_wsz); break;
|
||||
case 't': scanmult (opt, &caml_trace_level); break;
|
||||
case 'v': scanmult (opt, &caml_verb_gc); break;
|
||||
case 'w': scanmult (opt, &caml_init_major_window); break;
|
||||
case 'W': scanmult (opt, &caml_runtime_warnings); break;
|
||||
}
|
||||
while (*opt != '\0'){
|
||||
|
|
|
@ -44,14 +44,15 @@
|
|||
#include "caml/alloc.h"
|
||||
#include "caml/debugger.h"
|
||||
#include "caml/fail.h"
|
||||
#include "caml/gc_ctrl.h"
|
||||
#include "caml/instruct.h"
|
||||
#include "caml/io.h"
|
||||
#include "caml/misc.h"
|
||||
#include "caml/mlvalues.h"
|
||||
#include "caml/osdeps.h"
|
||||
#include "caml/signals.h"
|
||||
#include "caml/stacks.h"
|
||||
#include "caml/sys.h"
|
||||
#include "caml/gc_ctrl.h"
|
||||
#include "caml/io.h"
|
||||
|
||||
static char * error_message(void)
|
||||
{
|
||||
|
@ -135,6 +136,7 @@ CAMLprim value caml_sys_exit(value retcode)
|
|||
#ifndef NATIVE_CODE
|
||||
caml_debugger(PROGRAM_EXIT);
|
||||
#endif
|
||||
CAML_INSTR_ATEXIT ();
|
||||
exit(Int_val(retcode));
|
||||
return Val_unit;
|
||||
}
|
||||
|
|
|
@ -51,11 +51,7 @@ static void do_set (value ar, mlsize_t offset, value v)
|
|||
value old = Field (ar, offset);
|
||||
Field (ar, offset) = v;
|
||||
if (!(Is_block (old) && Is_young (old))){
|
||||
if (caml_weak_ref_table.ptr >= caml_weak_ref_table.limit){
|
||||
CAMLassert (caml_weak_ref_table.ptr == caml_weak_ref_table.limit);
|
||||
caml_realloc_ref_table (&caml_weak_ref_table);
|
||||
}
|
||||
*caml_weak_ref_table.ptr++ = &Field (ar, offset);
|
||||
add_to_ref_table (&caml_weak_ref_table, &Field (ar, offset));
|
||||
}
|
||||
}else{
|
||||
Field (ar, offset) = v;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue