-
Notifications
You must be signed in to change notification settings - Fork 790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Serialization and deserialization of the typechecking data #18162
base: feature/reuse-typechecking-results
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -337,6 +337,54 @@ let EncodeOptimizationData (tcGlobals, tcConfig: TcConfig, outfile, exportRemapp | |
else | ||
[] | ||
|
||
let GetTypecheckingData (file, ilScopeRef, ilModule, byteReaderA, byteReaderB) = | ||
|
||
let memA = byteReaderA () | ||
|
||
let memB = | ||
match byteReaderB with | ||
| None -> ByteMemory.Empty.AsReadOnly() | ||
| Some br -> br () | ||
|
||
unpickleObjWithDanglingCcus file ilScopeRef ilModule unpickleTcInfo memA memB | ||
|
||
let WriteTypecheckingData (tcConfig: TcConfig, tcGlobals, fileName, inMem, ccu, tcInfo) = | ||
|
||
// need to understand the naming and if we even want two resources here... | ||
let rName = "FSharpTypecheckingData" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need 2 resources here? |
||
let rNameB = "FSharpTypecheckingDataB" | ||
|
||
PickleToResource | ||
inMem | ||
fileName | ||
tcGlobals | ||
tcConfig.compressMetadata | ||
ccu | ||
(rName + ccu.AssemblyName) | ||
(rNameB + ccu.AssemblyName) | ||
pickleTcInfo | ||
tcInfo | ||
|
||
let EncodeTypecheckingData (tcConfig: TcConfig, tcGlobals, generatedCcu, outfile, isIncrementalBuild, tcInfo) = | ||
let r1, r2 = | ||
WriteTypecheckingData( | ||
tcConfig, | ||
tcGlobals, | ||
outfile, | ||
isIncrementalBuild, | ||
generatedCcu, | ||
tcInfo) | ||
|
||
let resources = | ||
[ | ||
r1 | ||
match r2 with | ||
| None -> () | ||
| Some r -> r | ||
] | ||
|
||
resources | ||
|
||
exception AssemblyNotResolved of originalName: string * range: range | ||
|
||
exception MSBuildReferenceResolutionWarning of message: string * warningCode: string * range: range | ||
|
@@ -901,17 +949,17 @@ type TcAssemblyResolutions(tcConfig: TcConfig, results: AssemblyResolution list, | |
|
||
for UnresolvedAssemblyReference (referenceText, _ranges) in unresolved do | ||
if referenceText.Contains("mscorlib") then | ||
Debug.Assert(false, sprintf "whoops, did not resolve mscorlib: '%s'%s" referenceText addedText) | ||
//Debug.Assert(false, sprintf "whoops, did not resolve mscorlib: '%s'%s" referenceText addedText) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For some reason this breaks some test mockery, didn't investigate yet. |
||
itFailed <- true | ||
|
||
for x in frameworkDLLs do | ||
if not (FileSystem.IsPathRootedShim(x.resolvedPath)) then | ||
Debug.Assert(false, sprintf "frameworkDLL should be absolute path: '%s'%s" x.resolvedPath addedText) | ||
//Debug.Assert(false, sprintf "frameworkDLL should be absolute path: '%s'%s" x.resolvedPath addedText) | ||
itFailed <- true | ||
|
||
for x in nonFrameworkReferences do | ||
if not (FileSystem.IsPathRootedShim(x.resolvedPath)) then | ||
Debug.Assert(false, sprintf "nonFrameworkReference should be absolute path: '%s'%s" x.resolvedPath addedText) | ||
//Debug.Assert(false, sprintf "nonFrameworkReference should be absolute path: '%s'%s" x.resolvedPath addedText) | ||
itFailed <- true | ||
|
||
if itFailed then | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -149,7 +149,8 @@ let TypeCheck | |
tcEnv0, | ||
openDecls0, | ||
inputs, | ||
exiter: Exiter | ||
exiter: Exiter, | ||
outfile: string | ||
) = | ||
try | ||
if isNil inputs then | ||
|
@@ -162,17 +163,69 @@ let TypeCheck | |
|
||
let eagerFormat (diag: PhasedDiagnostic) = diag.EagerlyFormatCore true | ||
|
||
CheckClosedInputSet( | ||
ctok, | ||
diagnosticsLogger.CheckForErrors, | ||
tcConfig, | ||
tcImports, | ||
tcGlobals, | ||
None, | ||
if false then | ||
// I don't know yet if this is the right thing even | ||
let assembly = (tcImports.DllTable.TryFind tcConfig.primaryAssembly.Name).Value | ||
|
||
// these should be restored from raw resources probably | ||
let byteReaderA () = ByteMemory.Empty.AsReadOnly() | ||
let byteReaderB = None | ||
|
||
let tcInfo = | ||
GetTypecheckingData( | ||
assembly.FileName, | ||
assembly.ILScopeRef, | ||
assembly.RawMetadata.TryGetILModuleDef(), | ||
byteReaderA, | ||
byteReaderB | ||
) | ||
|
||
let rawData = tcInfo.RawData | ||
|
||
let topAttrs = | ||
{ | ||
mainMethodAttrs = rawData.MainMethodAttrs | ||
netModuleAttrs = rawData.NetModuleAttrs | ||
assemblyAttrs = rawData.AssemblyAttrs | ||
} | ||
|
||
// need to understand if anything can be used here, pickling state is hard | ||
tcInitialState, | ||
eagerFormat, | ||
inputs | ||
) | ||
topAttrs, | ||
rawData.DeclaredImpls, | ||
// this is quite definitely wrong, need to figure out what to do with the environment | ||
tcInitialState.TcEnvFromImpls | ||
|
||
else | ||
let tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile = | ||
CheckClosedInputSet( | ||
ctok, | ||
diagnosticsLogger.CheckForErrors, | ||
tcConfig, | ||
tcImports, | ||
tcGlobals, | ||
None, | ||
tcInitialState, | ||
eagerFormat, | ||
inputs | ||
) | ||
|
||
if false then | ||
let tcInfo = | ||
{ | ||
MainMethodAttrs = topAttrs.mainMethodAttrs | ||
NetModuleAttrs = topAttrs.netModuleAttrs | ||
AssemblyAttrs = topAttrs.assemblyAttrs | ||
DeclaredImpls = declaredImpls | ||
} | ||
|
||
// will need to pass results further somewhere | ||
let _typecheckingDataResources = | ||
EncodeTypecheckingData(tcConfig, tcGlobals, tcState.Ccu, outfile, false, tcInfo) | ||
|
||
() | ||
|
||
tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So out of 4 things, |
||
with exn -> | ||
errorRecovery exn rangeStartup | ||
exiter.Exit 1 | ||
|
@@ -693,7 +746,7 @@ let main1 | |
let inputs = inputs |> List.map fst | ||
|
||
let tcState, topAttrs, typedAssembly, _tcEnvAtEnd = | ||
TypeCheck(ctok, tcConfig, tcImports, tcGlobals, diagnosticsLogger, assemblyName, tcEnv0, openDecls0, inputs, exiter) | ||
TypeCheck(ctok, tcConfig, tcImports, tcGlobals, diagnosticsLogger, assemblyName, tcEnv0, openDecls0, inputs, exiter, outfile) | ||
|
||
AbortOnError(diagnosticsLogger, exiter) | ||
ReportTime tcConfig "Typechecked" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5899,6 +5899,12 @@ type PickledCcuInfo = | |
|
||
override _.ToString() = "PickledCcuInfo(...)" | ||
|
||
type PickledTcInfo = { | ||
MainMethodAttrs: Attribs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW would a similar problem for things like TcEnv or TcState |
||
NetModuleAttrs: Attribs | ||
AssemblyAttrs: Attribs | ||
DeclaredImpls: CheckedImplFile list | ||
} | ||
|
||
/// Represents a set of free local values. Computed and cached by later phases | ||
/// (never cached type checking). Cached in expressions. Not pickled. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2177,6 +2177,32 @@ and p_modul_typ (x: ModuleOrNamespaceType) st = | |
(x.ModuleOrNamespaceKind, x.AllValsAndMembers, x.AllEntities) | ||
st | ||
|
||
and p_qualified_name_of_file qualifiedNameOfFile st = | ||
let (QualifiedNameOfFile ident) = qualifiedNameOfFile | ||
p_ident ident st | ||
|
||
and p_pragma pragma st = | ||
let (ScopedPragma.WarningOff (range, warningNumber)) = pragma | ||
p_range range st | ||
p_int warningNumber st | ||
|
||
and p_pragmas x st = | ||
p_list p_pragma x st | ||
|
||
and p_checked_impl_file (file: CheckedImplFile) st = | ||
p_tup5 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this doesn't pickle all the info yet, see the comment for unpickling. |
||
p_qualified_name_of_file | ||
p_pragmas | ||
p_modul_typ | ||
p_bool | ||
p_bool | ||
(file.QualifiedNameOfFile, | ||
file.Pragmas, | ||
file.Signature, | ||
file.HasExplicitEntryPoint, | ||
file.IsScript) | ||
st | ||
|
||
and u_tycon_repr st = | ||
let tag1 = u_byte st | ||
match tag1 with | ||
|
@@ -2510,6 +2536,40 @@ and u_modul_typ st = | |
(u_qlist u_entity_spec) st | ||
ModuleOrNamespaceType(x1, x3, x5) | ||
|
||
and u_qualified_name_of_file st = | ||
let ident = u_ident st | ||
QualifiedNameOfFile(ident) | ||
|
||
and u_pragma st = | ||
let range = u_range st | ||
let warningNumber = u_int st | ||
ScopedPragma.WarningOff (range, warningNumber) | ||
|
||
and u_pragmas st = | ||
u_list u_pragma st | ||
|
||
and u_checked_impl_file st = | ||
let qualifiedNameOfFile, pragmas, signature, hasExplicitEntryPoint, isScript = | ||
u_tup5 | ||
u_qualified_name_of_file | ||
u_pragmas | ||
u_modul_typ | ||
u_bool | ||
u_bool | ||
st | ||
|
||
CheckedImplFile( | ||
qualifiedNameOfFile, | ||
pragmas, | ||
signature, | ||
// ModuleOrNamespaceContents needs implementing, feels hard but doable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where the continuous progress should be happening. |
||
Unchecked.defaultof<_>, | ||
hasExplicitEntryPoint, | ||
isScript, | ||
// this anon record map can be likely easily built in top of primitives here | ||
Unchecked.defaultof<_>, | ||
// something about debug points, not sure we care here | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do care to have debugging working in VS, yes :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After all, this whole story is to speed up the inner dev loop, which is happening in DEBUG mode normally. |
||
Unchecked.defaultof<_>) | ||
|
||
//--------------------------------------------------------------------------- | ||
// Pickle/unpickle for F# expressions (for optimization data) | ||
|
@@ -2906,8 +2966,29 @@ let pickleModuleOrNamespace mspec st = p_entity_spec mspec st | |
let pickleCcuInfo (minfo: PickledCcuInfo) st = | ||
p_tup4 pickleModuleOrNamespace p_string p_bool (p_space 3) (minfo.mspec, minfo.compileTimeWorkingDir, minfo.usesQuotations, ()) st | ||
|
||
let pickleTcInfo (tcInfo: PickledTcInfo) (st: WriterState) = | ||
p_attribs tcInfo.MainMethodAttrs st | ||
p_attribs tcInfo.NetModuleAttrs st | ||
p_attribs tcInfo.AssemblyAttrs st | ||
|
||
p_list p_checked_impl_file tcInfo.DeclaredImpls st | ||
|
||
let unpickleModuleOrNamespace st = u_entity_spec st | ||
|
||
let unpickleCcuInfo st = | ||
let a, b, c, _space = u_tup4 unpickleModuleOrNamespace u_string u_bool (u_space 3) st | ||
{ mspec=a; compileTimeWorkingDir=b; usesQuotations=c } | ||
|
||
let unpickleTcInfo st : PickledTcInfo = | ||
let mainMethodAttrs = u_attribs st | ||
let netModuleAttrs = u_attribs st | ||
let assemblyAttrs = u_attribs st | ||
|
||
let declaredImpls = u_list u_checked_impl_file st | ||
|
||
{ | ||
MainMethodAttrs = mainMethodAttrs | ||
NetModuleAttrs = netModuleAttrs | ||
AssemblyAttrs = assemblyAttrs | ||
DeclaredImpls = declaredImpls | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is byteReaderB good for here?
I think it should work without it.
UNLESS it is needed to reuse the most of existing pickling for signatures. Then of course let's have it for the sake of not reimplementing what has been done.