@jhlagado/azm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +649 -0
- package/README.md +142 -0
- package/dist/src/analysis.d.ts +11 -0
- package/dist/src/analysis.js +41 -0
- package/dist/src/api-compile.d.ts +8 -0
- package/dist/src/api-compile.js +3 -0
- package/dist/src/api-tooling.d.ts +25 -0
- package/dist/src/api-tooling.js +21 -0
- package/dist/src/cli.d.ts +30 -0
- package/dist/src/cli.js +523 -0
- package/dist/src/compile.d.ts +10 -0
- package/dist/src/compile.js +175 -0
- package/dist/src/compileShared.d.ts +3 -0
- package/dist/src/compileShared.js +7 -0
- package/dist/src/diagnosticTypes.d.ts +77 -0
- package/dist/src/diagnosticTypes.js +53 -0
- package/dist/src/formats/index.d.ts +7 -0
- package/dist/src/formats/index.js +17 -0
- package/dist/src/formats/range.d.ts +17 -0
- package/dist/src/formats/range.js +45 -0
- package/dist/src/formats/types.d.ts +208 -0
- package/dist/src/formats/types.js +1 -0
- package/dist/src/formats/writeAsm80.d.ts +6 -0
- package/dist/src/formats/writeAsm80.js +86 -0
- package/dist/src/formats/writeBin.d.ts +7 -0
- package/dist/src/formats/writeBin.js +23 -0
- package/dist/src/formats/writeD8m.d.ts +9 -0
- package/dist/src/formats/writeD8m.js +239 -0
- package/dist/src/formats/writeHex.d.ts +9 -0
- package/dist/src/formats/writeHex.js +39 -0
- package/dist/src/formats/writeListing.d.ts +8 -0
- package/dist/src/formats/writeListing.js +83 -0
- package/dist/src/frontend/asm80/asmLine.d.ts +39 -0
- package/dist/src/frontend/asm80/asmLine.js +89 -0
- package/dist/src/frontend/asm80/parseAsmRawValues.d.ts +4 -0
- package/dist/src/frontend/asm80/parseAsmRawValues.js +94 -0
- package/dist/src/frontend/asm80/quoteScan.d.ts +10 -0
- package/dist/src/frontend/asm80/quoteScan.js +25 -0
- package/dist/src/frontend/ast.d.ts +376 -0
- package/dist/src/frontend/ast.js +1 -0
- package/dist/src/frontend/directiveAliases.d.ts +14 -0
- package/dist/src/frontend/directiveAliases.js +189 -0
- package/dist/src/frontend/grammarData.d.ts +14 -0
- package/dist/src/frontend/grammarData.js +65 -0
- package/dist/src/frontend/immExprUtils.d.ts +2 -0
- package/dist/src/frontend/immExprUtils.js +12 -0
- package/dist/src/frontend/parseAsmFlatDirectiveLine.d.ts +17 -0
- package/dist/src/frontend/parseAsmFlatDirectiveLine.js +187 -0
- package/dist/src/frontend/parseAsmInstruction.d.ts +3 -0
- package/dist/src/frontend/parseAsmInstruction.js +73 -0
- package/dist/src/frontend/parseAsmStatements.d.ts +6 -0
- package/dist/src/frontend/parseAsmStatements.js +16 -0
- package/dist/src/frontend/parseAsmStream.d.ts +10 -0
- package/dist/src/frontend/parseAsmStream.js +33 -0
- package/dist/src/frontend/parseAsmTopLevel.d.ts +18 -0
- package/dist/src/frontend/parseAsmTopLevel.js +34 -0
- package/dist/src/frontend/parseDiagnostics.d.ts +9 -0
- package/dist/src/frontend/parseDiagnostics.js +16 -0
- package/dist/src/frontend/parseEnum.d.ts +12 -0
- package/dist/src/frontend/parseEnum.js +70 -0
- package/dist/src/frontend/parseImm.d.ts +10 -0
- package/dist/src/frontend/parseImm.js +397 -0
- package/dist/src/frontend/parseLogicalLines.d.ts +11 -0
- package/dist/src/frontend/parseLogicalLines.js +94 -0
- package/dist/src/frontend/parseOp.d.ts +25 -0
- package/dist/src/frontend/parseOp.js +120 -0
- package/dist/src/frontend/parseOpHeader.d.ts +20 -0
- package/dist/src/frontend/parseOpHeader.js +32 -0
- package/dist/src/frontend/parseOperands.d.ts +4 -0
- package/dist/src/frontend/parseOperands.js +290 -0
- package/dist/src/frontend/parseParams.d.ts +6 -0
- package/dist/src/frontend/parseParams.js +62 -0
- package/dist/src/frontend/parseParserRecovery.d.ts +12 -0
- package/dist/src/frontend/parseParserRecovery.js +17 -0
- package/dist/src/frontend/parseParserShared.d.ts +2 -0
- package/dist/src/frontend/parseParserShared.js +8 -0
- package/dist/src/frontend/parseRawDataDirectiveStart.d.ts +1 -0
- package/dist/src/frontend/parseRawDataDirectiveStart.js +3 -0
- package/dist/src/frontend/parseRawDataDirectives.d.ts +7 -0
- package/dist/src/frontend/parseRawDataDirectives.js +142 -0
- package/dist/src/frontend/parseRecordFieldDecl.d.ts +34 -0
- package/dist/src/frontend/parseRecordFieldDecl.js +177 -0
- package/dist/src/frontend/parseSourceItemDispatch.d.ts +46 -0
- package/dist/src/frontend/parseSourceItemDispatch.js +53 -0
- package/dist/src/frontend/parseSourceItemTable.d.ts +16 -0
- package/dist/src/frontend/parseSourceItemTable.js +68 -0
- package/dist/src/frontend/parseTopLevelCommon.d.ts +11 -0
- package/dist/src/frontend/parseTopLevelCommon.js +50 -0
- package/dist/src/frontend/parseTypes.d.ts +24 -0
- package/dist/src/frontend/parseTypes.js +77 -0
- package/dist/src/frontend/parser.d.ts +11 -0
- package/dist/src/frontend/parser.js +88 -0
- package/dist/src/frontend/source.d.ts +28 -0
- package/dist/src/frontend/source.js +58 -0
- package/dist/src/frontend/sourceExtensions.d.ts +2 -0
- package/dist/src/frontend/sourceExtensions.js +6 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +2 -0
- package/dist/src/lintCaseStyle.d.ts +4 -0
- package/dist/src/lintCaseStyle.js +129 -0
- package/dist/src/lowering/asmDirectiveLowering.d.ts +4 -0
- package/dist/src/lowering/asmDirectiveLowering.js +229 -0
- package/dist/src/lowering/asmDirectiveTraversal.d.ts +47 -0
- package/dist/src/lowering/asmDirectiveTraversal.js +52 -0
- package/dist/src/lowering/asmEquResolution.d.ts +8 -0
- package/dist/src/lowering/asmEquResolution.js +69 -0
- package/dist/src/lowering/asmInstructionLdHelpers.d.ts +15 -0
- package/dist/src/lowering/asmInstructionLdHelpers.js +102 -0
- package/dist/src/lowering/asmInstructionLowering.d.ts +5 -0
- package/dist/src/lowering/asmInstructionLowering.js +54 -0
- package/dist/src/lowering/asmInstructionStream.d.ts +46 -0
- package/dist/src/lowering/asmInstructionStream.js +51 -0
- package/dist/src/lowering/asmLoweringBranchCall.d.ts +43 -0
- package/dist/src/lowering/asmLoweringBranchCall.js +254 -0
- package/dist/src/lowering/asmLoweringHost.d.ts +23 -0
- package/dist/src/lowering/asmLoweringHost.js +1 -0
- package/dist/src/lowering/asmLoweringLd.d.ts +28 -0
- package/dist/src/lowering/asmLoweringLd.js +144 -0
- package/dist/src/lowering/asmRangeLowering.d.ts +17 -0
- package/dist/src/lowering/asmRangeLowering.js +39 -0
- package/dist/src/lowering/asmRawDataLowering.d.ts +16 -0
- package/dist/src/lowering/asmRawDataLowering.js +209 -0
- package/dist/src/lowering/asmSourceEmitter.d.ts +4 -0
- package/dist/src/lowering/asmSourceEmitter.js +9 -0
- package/dist/src/lowering/asmSourceInstructionLowering.d.ts +4 -0
- package/dist/src/lowering/asmSourceInstructionLowering.js +14 -0
- package/dist/src/lowering/asmUtils.d.ts +13 -0
- package/dist/src/lowering/asmUtils.js +105 -0
- package/dist/src/lowering/assemblerFlowSetup.d.ts +54 -0
- package/dist/src/lowering/assemblerFlowSetup.js +128 -0
- package/dist/src/lowering/assemblerLoweringContext.d.ts +151 -0
- package/dist/src/lowering/assemblerLoweringContext.js +16 -0
- package/dist/src/lowering/assemblerLoweringContextSplit.d.ts +7 -0
- package/dist/src/lowering/assemblerLoweringContextSplit.js +75 -0
- package/dist/src/lowering/assemblerLoweringPhases.d.ts +66 -0
- package/dist/src/lowering/assemblerLoweringPhases.js +166 -0
- package/dist/src/lowering/bytePlacement.d.ts +7 -0
- package/dist/src/lowering/bytePlacement.js +37 -0
- package/dist/src/lowering/capabilities.d.ts +67 -0
- package/dist/src/lowering/capabilities.js +1 -0
- package/dist/src/lowering/eaResolution.d.ts +58 -0
- package/dist/src/lowering/eaResolution.js +159 -0
- package/dist/src/lowering/emissionCore.d.ts +17 -0
- package/dist/src/lowering/emissionCore.js +21 -0
- package/dist/src/lowering/emit.d.ts +17 -0
- package/dist/src/lowering/emit.js +46 -0
- package/dist/src/lowering/emitContextBuilder.d.ts +63 -0
- package/dist/src/lowering/emitContextBuilder.js +41 -0
- package/dist/src/lowering/emitFinalization.d.ts +61 -0
- package/dist/src/lowering/emitFinalization.js +66 -0
- package/dist/src/lowering/emitFinalizationSetup.d.ts +19 -0
- package/dist/src/lowering/emitFinalizationSetup.js +26 -0
- package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.d.ts +7 -0
- package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.js +119 -0
- package/dist/src/lowering/emitPhase1Helpers.d.ts +4 -0
- package/dist/src/lowering/emitPhase1Helpers.js +12 -0
- package/dist/src/lowering/emitPhase1Types.d.ts +21 -0
- package/dist/src/lowering/emitPhase1Types.js +1 -0
- package/dist/src/lowering/emitPhase1WirePipeline.d.ts +70 -0
- package/dist/src/lowering/emitPhase1WirePipeline.js +203 -0
- package/dist/src/lowering/emitPhase1Workspace.d.ts +82 -0
- package/dist/src/lowering/emitPhase1Workspace.js +55 -0
- package/dist/src/lowering/emitPipeline.d.ts +121 -0
- package/dist/src/lowering/emitPipeline.js +57 -0
- package/dist/src/lowering/emitProgramContext.d.ts +39 -0
- package/dist/src/lowering/emitProgramContext.js +29 -0
- package/dist/src/lowering/emitState.d.ts +90 -0
- package/dist/src/lowering/emitState.js +124 -0
- package/dist/src/lowering/fixupBaseResolution.d.ts +7 -0
- package/dist/src/lowering/fixupBaseResolution.js +23 -0
- package/dist/src/lowering/fixupEmission.d.ts +64 -0
- package/dist/src/lowering/fixupEmission.js +199 -0
- package/dist/src/lowering/immMath.d.ts +2 -0
- package/dist/src/lowering/immMath.js +34 -0
- package/dist/src/lowering/inputAssets.d.ts +7 -0
- package/dist/src/lowering/inputAssets.js +106 -0
- package/dist/src/lowering/ldEncoding.d.ts +15 -0
- package/dist/src/lowering/ldEncoding.js +12 -0
- package/dist/src/lowering/ldEncodingRegMemHelpers.d.ts +5 -0
- package/dist/src/lowering/ldEncodingRegMemHelpers.js +124 -0
- package/dist/src/lowering/ldFormSelection.d.ts +26 -0
- package/dist/src/lowering/ldFormSelection.js +92 -0
- package/dist/src/lowering/ldLowering.d.ts +7 -0
- package/dist/src/lowering/ldLowering.js +13 -0
- package/dist/src/lowering/loweredAsmByteEmission.d.ts +23 -0
- package/dist/src/lowering/loweredAsmByteEmission.js +185 -0
- package/dist/src/lowering/loweredAsmPlacement.d.ts +13 -0
- package/dist/src/lowering/loweredAsmPlacement.js +86 -0
- package/dist/src/lowering/loweredAsmStreamRecording.d.ts +27 -0
- package/dist/src/lowering/loweredAsmStreamRecording.js +215 -0
- package/dist/src/lowering/loweredAsmTypes.d.ts +202 -0
- package/dist/src/lowering/loweredAsmTypes.js +1 -0
- package/dist/src/lowering/loweredFormat.d.ts +3 -0
- package/dist/src/lowering/loweredFormat.js +26 -0
- package/dist/src/lowering/loweredItemSize.d.ts +4 -0
- package/dist/src/lowering/loweredItemSize.js +17 -0
- package/dist/src/lowering/loweringDiagnostics.d.ts +9 -0
- package/dist/src/lowering/loweringDiagnostics.js +55 -0
- package/dist/src/lowering/loweringTypes.d.ts +27 -0
- package/dist/src/lowering/loweringTypes.js +1 -0
- package/dist/src/lowering/opCandidateRegistry.d.ts +9 -0
- package/dist/src/lowering/opCandidateRegistry.js +9 -0
- package/dist/src/lowering/opExpansionExecution.d.ts +15 -0
- package/dist/src/lowering/opExpansionExecution.js +45 -0
- package/dist/src/lowering/opExpansionOrchestration.d.ts +15 -0
- package/dist/src/lowering/opExpansionOrchestration.js +88 -0
- package/dist/src/lowering/opExpansionStream.d.ts +12 -0
- package/dist/src/lowering/opExpansionStream.js +176 -0
- package/dist/src/lowering/opMatching.d.ts +52 -0
- package/dist/src/lowering/opMatching.js +355 -0
- package/dist/src/lowering/opSubstitution.d.ts +13 -0
- package/dist/src/lowering/opSubstitution.js +175 -0
- package/dist/src/lowering/prescanTypes.d.ts +7 -0
- package/dist/src/lowering/prescanTypes.js +1 -0
- package/dist/src/lowering/programLowering.d.ts +144 -0
- package/dist/src/lowering/programLowering.js +2 -0
- package/dist/src/lowering/programLoweringDeclarations.d.ts +5 -0
- package/dist/src/lowering/programLoweringDeclarations.js +5 -0
- package/dist/src/lowering/programLoweringFinalize.d.ts +18 -0
- package/dist/src/lowering/programLoweringFinalize.js +115 -0
- package/dist/src/lowering/programLoweringTraversal.d.ts +2 -0
- package/dist/src/lowering/programLoweringTraversal.js +93 -0
- package/dist/src/lowering/programPrescan.d.ts +3 -0
- package/dist/src/lowering/programPrescan.js +37 -0
- package/dist/src/lowering/traceFormat.d.ts +13 -0
- package/dist/src/lowering/traceFormat.js +211 -0
- package/dist/src/pathCompare.d.ts +3 -0
- package/dist/src/pathCompare.js +26 -0
- package/dist/src/pipeline.d.ts +78 -0
- package/dist/src/pipeline.js +1 -0
- package/dist/src/registerCare/analyze.d.ts +24 -0
- package/dist/src/registerCare/analyze.js +327 -0
- package/dist/src/registerCare/annotate.d.ts +11 -0
- package/dist/src/registerCare/annotate.js +76 -0
- package/dist/src/registerCare/boundaryHints.d.ts +2 -0
- package/dist/src/registerCare/boundaryHints.js +10 -0
- package/dist/src/registerCare/carriers.d.ts +4 -0
- package/dist/src/registerCare/carriers.js +78 -0
- package/dist/src/registerCare/controlFlow.d.ts +5 -0
- package/dist/src/registerCare/controlFlow.js +35 -0
- package/dist/src/registerCare/fix.d.ts +11 -0
- package/dist/src/registerCare/fix.js +119 -0
- package/dist/src/registerCare/liveness.d.ts +7 -0
- package/dist/src/registerCare/liveness.js +227 -0
- package/dist/src/registerCare/profiles.d.ts +11 -0
- package/dist/src/registerCare/profiles.js +45 -0
- package/dist/src/registerCare/programModel.d.ts +3 -0
- package/dist/src/registerCare/programModel.js +181 -0
- package/dist/src/registerCare/report.d.ts +5 -0
- package/dist/src/registerCare/report.js +139 -0
- package/dist/src/registerCare/smartComments.d.ts +5 -0
- package/dist/src/registerCare/smartComments.js +247 -0
- package/dist/src/registerCare/sourceText.d.ts +8 -0
- package/dist/src/registerCare/sourceText.js +15 -0
- package/dist/src/registerCare/summary.d.ts +3 -0
- package/dist/src/registerCare/summary.js +492 -0
- package/dist/src/registerCare/tooling.d.ts +42 -0
- package/dist/src/registerCare/tooling.js +50 -0
- package/dist/src/registerCare/types.d.ts +154 -0
- package/dist/src/registerCare/types.js +1 -0
- package/dist/src/semantics/declVisitor.d.ts +5 -0
- package/dist/src/semantics/declVisitor.js +11 -0
- package/dist/src/semantics/env.d.ts +28 -0
- package/dist/src/semantics/env.js +432 -0
- package/dist/src/semantics/layout.d.ts +21 -0
- package/dist/src/semantics/layout.js +226 -0
- package/dist/src/semantics/layoutCastFold.d.ts +22 -0
- package/dist/src/semantics/layoutCastFold.js +118 -0
- package/dist/src/semantics/semanticsDiagnostics.d.ts +2 -0
- package/dist/src/semantics/semanticsDiagnostics.js +4 -0
- package/dist/src/semantics/typeQueries.d.ts +31 -0
- package/dist/src/semantics/typeQueries.js +124 -0
- package/dist/src/sourceIncludeExpansion.d.ts +17 -0
- package/dist/src/sourceIncludeExpansion.js +124 -0
- package/dist/src/sourceIncludePaths.d.ts +1 -0
- package/dist/src/sourceIncludePaths.js +12 -0
- package/dist/src/sourceLoader.d.ts +15 -0
- package/dist/src/sourceLoader.js +118 -0
- package/dist/src/z80/effects.d.ts +3 -0
- package/dist/src/z80/effects.js +516 -0
- package/dist/src/z80/encode.d.ts +10 -0
- package/dist/src/z80/encode.js +412 -0
- package/dist/src/z80/encodeAlu.d.ts +7 -0
- package/dist/src/z80/encodeAlu.js +219 -0
- package/dist/src/z80/encodeBitOps.d.ts +7 -0
- package/dist/src/z80/encodeBitOps.js +123 -0
- package/dist/src/z80/encodeContext.d.ts +29 -0
- package/dist/src/z80/encodeContext.js +1 -0
- package/dist/src/z80/encodeControl.d.ts +26 -0
- package/dist/src/z80/encodeControl.js +180 -0
- package/dist/src/z80/encodeCoreOps.d.ts +7 -0
- package/dist/src/z80/encodeCoreOps.js +131 -0
- package/dist/src/z80/encodeIo.d.ts +9 -0
- package/dist/src/z80/encodeIo.js +128 -0
- package/dist/src/z80/encodeLd.d.ts +13 -0
- package/dist/src/z80/encodeLd.js +273 -0
- package/dist/src/z80/encoderRegistry.d.ts +13 -0
- package/dist/src/z80/encoderRegistry.js +169 -0
- package/docs/reference/cli.md +134 -0
- package/docs/reference/tooling-api.md +248 -0
- package/package.json +98 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { renderRegisterCareSourceBlock } from './report.js';
|
|
2
|
+
import { joinSourceLines, splitSourceLines } from './sourceText.js';
|
|
3
|
+
const GENERATED_COMPACT_LINE_RE = /^\s*;\s*!\s*(?:in|out|maybe-out|clobbers|preserves)(?:\s|$)/i;
|
|
4
|
+
function isCommentLine(line) {
|
|
5
|
+
return /^\s*;/.test(line);
|
|
6
|
+
}
|
|
7
|
+
function isGeneratedCompactLine(line) {
|
|
8
|
+
return GENERATED_COMPACT_LINE_RE.test(line);
|
|
9
|
+
}
|
|
10
|
+
function precedingCommentBlockStart(lines, labelIndex) {
|
|
11
|
+
let index = labelIndex - 1;
|
|
12
|
+
if (index < 0 || !isCommentLine(lines[index] ?? ''))
|
|
13
|
+
return undefined;
|
|
14
|
+
while (index >= 0 && isCommentLine(lines[index] ?? ''))
|
|
15
|
+
index -= 1;
|
|
16
|
+
return index + 1;
|
|
17
|
+
}
|
|
18
|
+
function generatedBlockBeforeLabel(lines, labelIndex) {
|
|
19
|
+
let compactStart = labelIndex;
|
|
20
|
+
while (compactStart > 0 && isGeneratedCompactLine(lines[compactStart - 1] ?? '')) {
|
|
21
|
+
compactStart -= 1;
|
|
22
|
+
}
|
|
23
|
+
if (compactStart < labelIndex)
|
|
24
|
+
return { start: compactStart, end: labelIndex - 1 };
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
function hasPrecedingCommentBlock(lines, labelIndex) {
|
|
28
|
+
return precedingCommentBlockStart(lines, labelIndex) !== undefined;
|
|
29
|
+
}
|
|
30
|
+
function isExplicitEntryRoutine(routine) {
|
|
31
|
+
return routine.entryLabels?.includes(routine.name) === true;
|
|
32
|
+
}
|
|
33
|
+
function annotateFile(source, routines) {
|
|
34
|
+
const sourceLines = splitSourceLines(source);
|
|
35
|
+
const { lines } = sourceLines;
|
|
36
|
+
const sorted = [...routines].sort((a, b) => b.routine.span.start.line - a.routine.span.start.line);
|
|
37
|
+
for (const item of sorted) {
|
|
38
|
+
const labelIndex = item.routine.span.start.line - 1;
|
|
39
|
+
if (labelIndex < 0 || labelIndex > lines.length)
|
|
40
|
+
continue;
|
|
41
|
+
const block = renderRegisterCareSourceBlock(item.summary);
|
|
42
|
+
const hasContractContent = block.length > 0;
|
|
43
|
+
const existing = generatedBlockBeforeLabel(lines, labelIndex);
|
|
44
|
+
if (existing) {
|
|
45
|
+
lines.splice(existing.start, existing.end - existing.start + 1, ...(hasContractContent ? block : []));
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (!hasContractContent)
|
|
49
|
+
continue;
|
|
50
|
+
if (!isExplicitEntryRoutine(item.routine) && !hasPrecedingCommentBlock(lines, labelIndex)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
lines.splice(labelIndex, 0, ...block);
|
|
54
|
+
}
|
|
55
|
+
return joinSourceLines(sourceLines);
|
|
56
|
+
}
|
|
57
|
+
export function annotateRegisterCareContracts(sourceTexts, routines) {
|
|
58
|
+
const byFile = new Map();
|
|
59
|
+
for (const item of routines) {
|
|
60
|
+
if (!sourceTexts.has(item.routine.span.file))
|
|
61
|
+
continue;
|
|
62
|
+
const items = byFile.get(item.routine.span.file) ?? [];
|
|
63
|
+
items.push(item);
|
|
64
|
+
byFile.set(item.routine.span.file, items);
|
|
65
|
+
}
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const [file, items] of [...byFile].sort(([a], [b]) => a.localeCompare(b))) {
|
|
68
|
+
const source = sourceTexts.get(file);
|
|
69
|
+
if (source === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
const text = annotateFile(source, items);
|
|
72
|
+
if (text !== source)
|
|
73
|
+
out.push({ path: file, text });
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function precedingCServiceName(item) {
|
|
2
|
+
const inst = item?.instruction;
|
|
3
|
+
if (!inst || inst.head.toLowerCase() !== 'ld' || inst.operands.length !== 2)
|
|
4
|
+
return undefined;
|
|
5
|
+
const dst = inst.operands[0];
|
|
6
|
+
const src = inst.operands[1];
|
|
7
|
+
if (dst?.kind !== 'Reg' || dst.name.toUpperCase() !== 'C')
|
|
8
|
+
return undefined;
|
|
9
|
+
return src?.kind === 'Imm' && src.expr.kind === 'ImmName' ? src.expr.name : undefined;
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RegisterCareUnit } from './types.js';
|
|
2
|
+
export declare function normalizeCarrierName(raw: string): RegisterCareUnit | undefined;
|
|
3
|
+
export declare function expandCarrier(raw: string): RegisterCareUnit[] | undefined;
|
|
4
|
+
export declare function expandCarrierList(raw: string[]): RegisterCareUnit[] | undefined;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const FLAG_UNITS = ['carry', 'zero', 'sign', 'parity', 'halfCarry'];
|
|
2
|
+
const SINGLE_UNITS = new Set([
|
|
3
|
+
'A',
|
|
4
|
+
'B',
|
|
5
|
+
'C',
|
|
6
|
+
'D',
|
|
7
|
+
'E',
|
|
8
|
+
'H',
|
|
9
|
+
'L',
|
|
10
|
+
'IXH',
|
|
11
|
+
'IXL',
|
|
12
|
+
'IYH',
|
|
13
|
+
'IYL',
|
|
14
|
+
'SPH',
|
|
15
|
+
'SPL',
|
|
16
|
+
'carry',
|
|
17
|
+
'zero',
|
|
18
|
+
'sign',
|
|
19
|
+
'parity',
|
|
20
|
+
'halfCarry',
|
|
21
|
+
]);
|
|
22
|
+
const PAIRS = {
|
|
23
|
+
AF: ['A', ...FLAG_UNITS],
|
|
24
|
+
BC: ['B', 'C'],
|
|
25
|
+
DE: ['D', 'E'],
|
|
26
|
+
HL: ['H', 'L'],
|
|
27
|
+
IX: ['IXH', 'IXL'],
|
|
28
|
+
IY: ['IYH', 'IYL'],
|
|
29
|
+
SP: ['SPH', 'SPL'],
|
|
30
|
+
};
|
|
31
|
+
const FLAG_ALIASES = {
|
|
32
|
+
CARRY: 'carry',
|
|
33
|
+
ZERO: 'zero',
|
|
34
|
+
Z: 'zero',
|
|
35
|
+
SIGN: 'sign',
|
|
36
|
+
S: 'sign',
|
|
37
|
+
PARITY: 'parity',
|
|
38
|
+
PV: 'parity',
|
|
39
|
+
'P/V': 'parity',
|
|
40
|
+
HALFCARRY: 'halfCarry',
|
|
41
|
+
HFLAG: 'halfCarry',
|
|
42
|
+
};
|
|
43
|
+
export function normalizeCarrierName(raw) {
|
|
44
|
+
const trimmed = raw.trim();
|
|
45
|
+
const upper = trimmed.toUpperCase();
|
|
46
|
+
const flag = FLAG_ALIASES[upper];
|
|
47
|
+
if (flag)
|
|
48
|
+
return flag;
|
|
49
|
+
if (SINGLE_UNITS.has(upper))
|
|
50
|
+
return upper;
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
export function expandCarrier(raw) {
|
|
54
|
+
const upper = raw.trim().toUpperCase();
|
|
55
|
+
if (upper === 'F')
|
|
56
|
+
return FLAG_UNITS;
|
|
57
|
+
const pair = PAIRS[upper];
|
|
58
|
+
if (pair)
|
|
59
|
+
return pair;
|
|
60
|
+
const single = normalizeCarrierName(raw);
|
|
61
|
+
return single ? [single] : undefined;
|
|
62
|
+
}
|
|
63
|
+
export function expandCarrierList(raw) {
|
|
64
|
+
const out = [];
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
for (const item of raw) {
|
|
67
|
+
const expanded = expandCarrier(item);
|
|
68
|
+
if (!expanded)
|
|
69
|
+
return undefined;
|
|
70
|
+
for (const unit of expanded) {
|
|
71
|
+
if (seen.has(unit))
|
|
72
|
+
continue;
|
|
73
|
+
seen.add(unit);
|
|
74
|
+
out.push(unit);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { InstructionEffect, RegisterCareRoutine } from './types.js';
|
|
2
|
+
export declare function labelIndex(routine: RegisterCareRoutine): Map<string, number>;
|
|
3
|
+
export declare function instructionSuccessors(routine: RegisterCareRoutine, index: number, effect: InstructionEffect, labels: ReadonlyMap<string, number>, options?: {
|
|
4
|
+
boundaryFallthrough?: boolean;
|
|
5
|
+
}): number[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function unique(items) {
|
|
2
|
+
return [...new Set(items)];
|
|
3
|
+
}
|
|
4
|
+
export function labelIndex(routine) {
|
|
5
|
+
const out = new Map();
|
|
6
|
+
routine.instructions.forEach((item, index) => {
|
|
7
|
+
for (const label of item.labels)
|
|
8
|
+
out.set(label, index);
|
|
9
|
+
});
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
function localTargetIndex(labels, target) {
|
|
13
|
+
if (!target)
|
|
14
|
+
return undefined;
|
|
15
|
+
return labels.get(target);
|
|
16
|
+
}
|
|
17
|
+
export function instructionSuccessors(routine, index, effect, labels, options = {}) {
|
|
18
|
+
const next = index + 1 < routine.instructions.length ? index + 1 : undefined;
|
|
19
|
+
if (effect.control.kind === 'fallthrough' ||
|
|
20
|
+
(options.boundaryFallthrough &&
|
|
21
|
+
(effect.control.kind === 'call' || effect.control.kind === 'rst'))) {
|
|
22
|
+
return next === undefined ? [] : [next];
|
|
23
|
+
}
|
|
24
|
+
if (effect.control.kind === 'jump') {
|
|
25
|
+
const target = localTargetIndex(labels, effect.control.target);
|
|
26
|
+
if (effect.control.conditional) {
|
|
27
|
+
return unique([
|
|
28
|
+
...(target === undefined ? [] : [target]),
|
|
29
|
+
...(next === undefined ? [] : [next]),
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
32
|
+
return target === undefined ? [] : [target];
|
|
33
|
+
}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RegisterCareOutputCandidate, RegisterCareRoutine, RegisterCareUnit } from './types.js';
|
|
2
|
+
interface RegisterCareExpectOutFix {
|
|
3
|
+
file: string;
|
|
4
|
+
line: number;
|
|
5
|
+
column: number;
|
|
6
|
+
routine: string;
|
|
7
|
+
carriers: RegisterCareUnit[];
|
|
8
|
+
}
|
|
9
|
+
export declare function findExpectOutFixes(routines: RegisterCareRoutine[], candidates: RegisterCareOutputCandidate[]): RegisterCareExpectOutFix[];
|
|
10
|
+
export declare function applyExpectOutFixesToSource(source: string, fixes: RegisterCareExpectOutFix[], referenceSource?: string): string;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { getZ80InstructionEffect } from '../z80/effects.js';
|
|
2
|
+
import { instructionSuccessors, labelIndex } from './controlFlow.js';
|
|
3
|
+
import { contractCarrierList } from './report.js';
|
|
4
|
+
import { joinSourceLines, splitSourceLines } from './sourceText.js';
|
|
5
|
+
function sameLocation(a, b) {
|
|
6
|
+
return a.file === b.file && a.line === b.line && a.column === b.column;
|
|
7
|
+
}
|
|
8
|
+
function isUnconditionalDirectCall(item) {
|
|
9
|
+
const effect = getZ80InstructionEffect(item.instruction);
|
|
10
|
+
return (effect.control.kind === 'call' &&
|
|
11
|
+
effect.control.target !== undefined &&
|
|
12
|
+
!effect.control.conditional);
|
|
13
|
+
}
|
|
14
|
+
function continuationReads(routine, callIndex, carriers) {
|
|
15
|
+
const labels = labelIndex(routine);
|
|
16
|
+
const confirmed = new Set();
|
|
17
|
+
const work = callIndex + 1 < routine.instructions.length
|
|
18
|
+
? [{ index: callIndex + 1, pending: [...new Set(carriers)] }]
|
|
19
|
+
: [];
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
let steps = 0;
|
|
22
|
+
while (work.length > 0 && steps < 512) {
|
|
23
|
+
steps += 1;
|
|
24
|
+
const state = work.pop();
|
|
25
|
+
const pending = state.pending.filter((unit) => !confirmed.has(unit));
|
|
26
|
+
if (pending.length === 0)
|
|
27
|
+
continue;
|
|
28
|
+
const key = `${state.index}:${pending.join(',')}`;
|
|
29
|
+
if (seen.has(key))
|
|
30
|
+
continue;
|
|
31
|
+
seen.add(key);
|
|
32
|
+
const item = routine.instructions[state.index];
|
|
33
|
+
if (!item)
|
|
34
|
+
continue;
|
|
35
|
+
const effect = getZ80InstructionEffect(item.instruction);
|
|
36
|
+
const reads = new Set(effect.reads);
|
|
37
|
+
const writes = new Set(effect.writes);
|
|
38
|
+
const remaining = [];
|
|
39
|
+
for (const unit of pending) {
|
|
40
|
+
if (reads.has(unit)) {
|
|
41
|
+
confirmed.add(unit);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!writes.has(unit))
|
|
45
|
+
remaining.push(unit);
|
|
46
|
+
}
|
|
47
|
+
if (remaining.length === 0)
|
|
48
|
+
continue;
|
|
49
|
+
for (const next of instructionSuccessors(routine, state.index, effect, labels)) {
|
|
50
|
+
work.push({ index: next, pending: remaining });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return carriers.filter((unit) => confirmed.has(unit));
|
|
54
|
+
}
|
|
55
|
+
export function findExpectOutFixes(routines, candidates) {
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const routine of routines) {
|
|
58
|
+
for (let index = 0; index < routine.instructions.length; index += 1) {
|
|
59
|
+
const item = routine.instructions[index];
|
|
60
|
+
if (!isUnconditionalDirectCall(item))
|
|
61
|
+
continue;
|
|
62
|
+
const candidate = candidates.find((entry) => sameLocation(item, entry));
|
|
63
|
+
if (!candidate)
|
|
64
|
+
continue;
|
|
65
|
+
const carriers = continuationReads(routine, index, candidate.carriers);
|
|
66
|
+
if (carriers.length === 0)
|
|
67
|
+
continue;
|
|
68
|
+
out.push({ ...candidate, carriers });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function isExpectOutLine(line) {
|
|
74
|
+
return /^\s*;\s*expects\s+out\b/i.test(line);
|
|
75
|
+
}
|
|
76
|
+
function expectedCallLine(originalLines, fix) {
|
|
77
|
+
return originalLines[fix.line - 1]?.trim();
|
|
78
|
+
}
|
|
79
|
+
function findCallLineIndex(lines, originalLines, fix) {
|
|
80
|
+
const expected = expectedCallLine(originalLines, fix);
|
|
81
|
+
if (!expected)
|
|
82
|
+
return undefined;
|
|
83
|
+
const preferred = fix.line - 1;
|
|
84
|
+
if (lines[preferred]?.trim() === expected)
|
|
85
|
+
return preferred;
|
|
86
|
+
let best;
|
|
87
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
88
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
89
|
+
if (lines[index]?.trim() !== expected)
|
|
90
|
+
continue;
|
|
91
|
+
const distance = Math.abs(index - preferred);
|
|
92
|
+
if (distance < bestDistance) {
|
|
93
|
+
best = index;
|
|
94
|
+
bestDistance = distance;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return best;
|
|
98
|
+
}
|
|
99
|
+
function indentation(line) {
|
|
100
|
+
return line.match(/^\s*/)?.[0] ?? '';
|
|
101
|
+
}
|
|
102
|
+
export function applyExpectOutFixesToSource(source, fixes, referenceSource = source) {
|
|
103
|
+
if (fixes.length === 0)
|
|
104
|
+
return source;
|
|
105
|
+
const originalLines = referenceSource.split(/\r?\n/);
|
|
106
|
+
const sourceLines = splitSourceLines(source);
|
|
107
|
+
const { lines } = sourceLines;
|
|
108
|
+
const sorted = [...fixes].sort((a, b) => b.line - a.line || b.column - a.column);
|
|
109
|
+
for (const fix of sorted) {
|
|
110
|
+
const index = findCallLineIndex(lines, originalLines, fix);
|
|
111
|
+
if (index === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
if (index > 0 && isExpectOutLine(lines[index - 1] ?? ''))
|
|
114
|
+
continue;
|
|
115
|
+
const prefix = indentation(lines[index] ?? '');
|
|
116
|
+
lines.splice(index, 0, `${prefix}; expects out ${contractCarrierList(fix.carriers)}`);
|
|
117
|
+
}
|
|
118
|
+
return joinSourceLines(sourceLines);
|
|
119
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Diagnostic } from '../diagnosticTypes.js';
|
|
2
|
+
import type { LocatedSmartComment, RegisterCareConflict, RegisterCareOutputCandidate, RegisterCareRoutine, RegisterCareUnit, RoutineSummary } from './types.js';
|
|
3
|
+
export declare function findRegisterCareConflicts(routine: RegisterCareRoutine, summaries: Map<string, RoutineSummary>, hints: LocatedSmartComment[]): RegisterCareConflict[];
|
|
4
|
+
export declare function findCallerOutputCandidateObservations(routines: RegisterCareRoutine[], summaries: Map<string, RoutineSummary>): RegisterCareOutputCandidate[];
|
|
5
|
+
export declare function findCallerOutputCandidates(routines: RegisterCareRoutine[], summaries: Map<string, RoutineSummary>): Map<string, RegisterCareUnit[]>;
|
|
6
|
+
export declare function findAcceptedOutputCandidatesFromHints(routines: RegisterCareRoutine[], summaries: Map<string, RoutineSummary>, hints: LocatedSmartComment[]): Map<string, RegisterCareUnit[]>;
|
|
7
|
+
export declare function diagnosticsForRegisterCareConflicts(conflicts: RegisterCareConflict[], severity: 'warning' | 'error'): Diagnostic[];
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { DiagnosticIds } from '../diagnosticTypes.js';
|
|
2
|
+
import { getZ80InstructionEffect } from '../z80/effects.js';
|
|
3
|
+
import { precedingCServiceName } from './boundaryHints.js';
|
|
4
|
+
import { instructionSuccessors, labelIndex } from './controlFlow.js';
|
|
5
|
+
import { rstServiceTargetName, rstTargetName } from './profiles.js';
|
|
6
|
+
function unique(units) {
|
|
7
|
+
return [...new Set(units)];
|
|
8
|
+
}
|
|
9
|
+
function withImpliedFlagUnits(units) {
|
|
10
|
+
return unique(units);
|
|
11
|
+
}
|
|
12
|
+
function boundaryTarget(routine, index, effect) {
|
|
13
|
+
const item = routine.instructions[index];
|
|
14
|
+
if (effect.control.kind === 'call' && effect.control.target) {
|
|
15
|
+
return {
|
|
16
|
+
targets: [effect.control.target],
|
|
17
|
+
conditional: effect.control.conditional,
|
|
18
|
+
subject: `CALL ${effect.control.target}`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (effect.control.kind === 'jump' &&
|
|
22
|
+
item?.head.toLowerCase() === 'jp' &&
|
|
23
|
+
!effect.control.conditional &&
|
|
24
|
+
effect.control.target &&
|
|
25
|
+
!effect.control.target.startsWith('.')) {
|
|
26
|
+
return {
|
|
27
|
+
targets: [effect.control.target],
|
|
28
|
+
conditional: false,
|
|
29
|
+
subject: `JP ${effect.control.target}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (effect.control.kind === 'rst' && effect.control.vector !== undefined) {
|
|
33
|
+
const target = rstTargetName(effect.control.vector);
|
|
34
|
+
const service = precedingCServiceName(routine.instructions[index - 1]);
|
|
35
|
+
const targets = service
|
|
36
|
+
? [rstServiceTargetName(effect.control.vector, service), target]
|
|
37
|
+
: [target];
|
|
38
|
+
return { targets, conditional: false, subject: target };
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
function summaryForBoundary(boundary, summaries) {
|
|
43
|
+
for (const target of boundary.targets) {
|
|
44
|
+
const summary = summaries.get(target);
|
|
45
|
+
if (summary)
|
|
46
|
+
return { target, summary };
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function hintUnitsForLine(hints, file, callLine) {
|
|
51
|
+
const prior = hints.find((hint) => hint.file === file && hint.line === callLine - 1 && hint.comment.kind === 'expectOut');
|
|
52
|
+
return prior?.comment.kind === 'expectOut' ? withImpliedFlagUnits(prior.comment.carriers) : [];
|
|
53
|
+
}
|
|
54
|
+
function outputUnits(summary) {
|
|
55
|
+
return withImpliedFlagUnits(summary.valueRelations.flatMap((relation) => relation.out));
|
|
56
|
+
}
|
|
57
|
+
function setEqual(left, right) {
|
|
58
|
+
if (left.size !== right.size)
|
|
59
|
+
return false;
|
|
60
|
+
for (const item of left)
|
|
61
|
+
if (!right.has(item))
|
|
62
|
+
return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function unionLive(sets) {
|
|
66
|
+
const out = new Set();
|
|
67
|
+
for (const set of sets) {
|
|
68
|
+
for (const unit of set)
|
|
69
|
+
out.add(unit);
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function transferLiveBefore(item, effect, boundary, summary, liveAfter, hints) {
|
|
74
|
+
const live = new Set(liveAfter);
|
|
75
|
+
if (boundary && summary) {
|
|
76
|
+
const accepted = new Set();
|
|
77
|
+
for (const unit of hintUnitsForLine(hints, item.file, item.line))
|
|
78
|
+
accepted.add(unit);
|
|
79
|
+
for (const unit of outputUnits(summary))
|
|
80
|
+
accepted.add(unit);
|
|
81
|
+
if (!boundary.conditional) {
|
|
82
|
+
for (const unit of summary.mayWrite)
|
|
83
|
+
live.delete(unit);
|
|
84
|
+
for (const unit of accepted)
|
|
85
|
+
live.delete(unit);
|
|
86
|
+
}
|
|
87
|
+
for (const unit of summary.mayRead)
|
|
88
|
+
live.add(unit);
|
|
89
|
+
}
|
|
90
|
+
const instructionWritesAreConditional = effect.control.kind === 'call' && effect.control.conditional;
|
|
91
|
+
if (!instructionWritesAreConditional) {
|
|
92
|
+
for (const unit of effect.writes)
|
|
93
|
+
live.delete(unit);
|
|
94
|
+
}
|
|
95
|
+
for (const unit of effect.reads)
|
|
96
|
+
live.add(unit);
|
|
97
|
+
return live;
|
|
98
|
+
}
|
|
99
|
+
function liveSetsForRoutine(routine, summaries, hints = []) {
|
|
100
|
+
const labels = labelIndex(routine);
|
|
101
|
+
const effects = routine.instructions.map((item) => getZ80InstructionEffect(item.instruction));
|
|
102
|
+
const boundaries = effects.map((effect, index) => boundaryTarget(routine, index, effect));
|
|
103
|
+
const resolvedSummaries = boundaries.map((boundary) => boundary ? summaryForBoundary(boundary, summaries)?.summary : undefined);
|
|
104
|
+
const successorIndexes = effects.map((effect, index) => instructionSuccessors(routine, index, effect, labels, { boundaryFallthrough: true }));
|
|
105
|
+
const liveIn = routine.instructions.map(() => new Set());
|
|
106
|
+
const liveOut = routine.instructions.map(() => new Set());
|
|
107
|
+
let changed = true;
|
|
108
|
+
let passes = 0;
|
|
109
|
+
while (changed && passes < Math.max(8, routine.instructions.length * 4)) {
|
|
110
|
+
changed = false;
|
|
111
|
+
passes += 1;
|
|
112
|
+
for (let index = routine.instructions.length - 1; index >= 0; index -= 1) {
|
|
113
|
+
const nextOut = unionLive(successorIndexes[index].map((successor) => liveIn[successor]));
|
|
114
|
+
const nextIn = transferLiveBefore(routine.instructions[index], effects[index], boundaries[index], resolvedSummaries[index], nextOut, hints);
|
|
115
|
+
if (!setEqual(nextOut, liveOut[index])) {
|
|
116
|
+
liveOut[index] = nextOut;
|
|
117
|
+
changed = true;
|
|
118
|
+
}
|
|
119
|
+
if (!setEqual(nextIn, liveIn[index])) {
|
|
120
|
+
liveIn[index] = nextIn;
|
|
121
|
+
changed = true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { liveIn, liveOut };
|
|
126
|
+
}
|
|
127
|
+
function resolvedBoundariesForRoutine(routine, summaries) {
|
|
128
|
+
const out = [];
|
|
129
|
+
for (let index = 0; index < routine.instructions.length; index += 1) {
|
|
130
|
+
const item = routine.instructions[index];
|
|
131
|
+
const effect = getZ80InstructionEffect(item.instruction);
|
|
132
|
+
const boundary = boundaryTarget(routine, index, effect);
|
|
133
|
+
if (!boundary)
|
|
134
|
+
continue;
|
|
135
|
+
const resolved = summaryForBoundary(boundary, summaries);
|
|
136
|
+
if (!resolved)
|
|
137
|
+
continue;
|
|
138
|
+
out.push({ item, index, boundary, target: resolved.target, summary: resolved.summary });
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
export function findRegisterCareConflicts(routine, summaries, hints) {
|
|
143
|
+
const conflicts = [];
|
|
144
|
+
const { liveOut } = liveSetsForRoutine(routine, summaries, hints);
|
|
145
|
+
for (const { item, index, boundary, target, summary } of resolvedBoundariesForRoutine(routine, summaries)) {
|
|
146
|
+
const accepted = new Set();
|
|
147
|
+
for (const unit of hintUnitsForLine(hints, item.file, item.line))
|
|
148
|
+
accepted.add(unit);
|
|
149
|
+
for (const unit of outputUnits(summary))
|
|
150
|
+
accepted.add(unit);
|
|
151
|
+
const carriers = unique(summary.mayWrite.filter((unit) => liveOut[index].has(unit) && !accepted.has(unit)));
|
|
152
|
+
if (carriers.length > 0) {
|
|
153
|
+
conflicts.push({
|
|
154
|
+
file: item.file,
|
|
155
|
+
line: item.line,
|
|
156
|
+
column: item.column,
|
|
157
|
+
callTarget: target,
|
|
158
|
+
carriers,
|
|
159
|
+
message: `${boundary.subject} may modify ${carriers.join(',')}, but the pre-call value is used later.`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return conflicts;
|
|
164
|
+
}
|
|
165
|
+
function appendMapUnits(out, target, units) {
|
|
166
|
+
const existing = out.get(target) ?? [];
|
|
167
|
+
for (const unit of units) {
|
|
168
|
+
if (!existing.includes(unit))
|
|
169
|
+
existing.push(unit);
|
|
170
|
+
}
|
|
171
|
+
out.set(target, existing);
|
|
172
|
+
}
|
|
173
|
+
function candidateMessage(boundary, units) {
|
|
174
|
+
const carriers = units.join(',');
|
|
175
|
+
const expectation = units.length === 1 ? units[0] : `{${carriers}}`;
|
|
176
|
+
return `${boundary.subject} writes ${carriers} and caller reads it later; review the call site and add \`; expects out ${expectation}\` above the call if this is intentional.`;
|
|
177
|
+
}
|
|
178
|
+
export function findCallerOutputCandidateObservations(routines, summaries) {
|
|
179
|
+
const out = [];
|
|
180
|
+
for (const routine of routines) {
|
|
181
|
+
const { liveOut } = liveSetsForRoutine(routine, summaries);
|
|
182
|
+
for (const { item, index, boundary, target, summary } of resolvedBoundariesForRoutine(routine, summaries)) {
|
|
183
|
+
const alreadyOutput = new Set(outputUnits(summary));
|
|
184
|
+
const carriers = unique(summary.mayWrite.filter((unit) => liveOut[index].has(unit) && !alreadyOutput.has(unit)));
|
|
185
|
+
if (carriers.length > 0) {
|
|
186
|
+
out.push({
|
|
187
|
+
file: item.file,
|
|
188
|
+
line: item.line,
|
|
189
|
+
column: item.column,
|
|
190
|
+
routine: target,
|
|
191
|
+
carriers,
|
|
192
|
+
message: candidateMessage(boundary, carriers),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return out;
|
|
198
|
+
}
|
|
199
|
+
export function findCallerOutputCandidates(routines, summaries) {
|
|
200
|
+
const candidates = new Map();
|
|
201
|
+
for (const item of findCallerOutputCandidateObservations(routines, summaries)) {
|
|
202
|
+
appendMapUnits(candidates, item.routine, item.carriers);
|
|
203
|
+
}
|
|
204
|
+
return candidates;
|
|
205
|
+
}
|
|
206
|
+
export function findAcceptedOutputCandidatesFromHints(routines, summaries, hints) {
|
|
207
|
+
const accepted = new Map();
|
|
208
|
+
for (const routine of routines) {
|
|
209
|
+
for (const { item, target } of resolvedBoundariesForRoutine(routine, summaries)) {
|
|
210
|
+
const units = hintUnitsForLine(hints, item.file, item.line);
|
|
211
|
+
if (units.length === 0)
|
|
212
|
+
continue;
|
|
213
|
+
appendMapUnits(accepted, target, units);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return accepted;
|
|
217
|
+
}
|
|
218
|
+
export function diagnosticsForRegisterCareConflicts(conflicts, severity) {
|
|
219
|
+
return conflicts.map((conflict) => ({
|
|
220
|
+
id: DiagnosticIds.RegisterCareConflict,
|
|
221
|
+
severity,
|
|
222
|
+
message: conflict.message,
|
|
223
|
+
file: conflict.file,
|
|
224
|
+
line: conflict.line,
|
|
225
|
+
column: conflict.column,
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RoutineSummary } from './types.js';
|
|
2
|
+
export type RegisterCareProfileName = 'mon3';
|
|
3
|
+
interface RegisterCareProfile {
|
|
4
|
+
name: RegisterCareProfileName;
|
|
5
|
+
rst: Map<number, RoutineSummary>;
|
|
6
|
+
rstServices: Map<string, RoutineSummary>;
|
|
7
|
+
}
|
|
8
|
+
export declare function rstTargetName(vector: number): string;
|
|
9
|
+
export declare function rstServiceTargetName(vector: number, service: string): string;
|
|
10
|
+
export declare function getRegisterCareProfile(name: RegisterCareProfileName | undefined): RegisterCareProfile | undefined;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const FLAG_UNITS = ['carry', 'zero', 'sign', 'parity', 'halfCarry'];
|
|
2
|
+
export function rstTargetName(vector) {
|
|
3
|
+
return `RST_$${vector.toString(16).toUpperCase().padStart(2, '0')}`;
|
|
4
|
+
}
|
|
5
|
+
function normalizeServiceName(service) {
|
|
6
|
+
return service.replace(/[^A-Za-z0-9]/gu, '').toUpperCase();
|
|
7
|
+
}
|
|
8
|
+
export function rstServiceTargetName(vector, service) {
|
|
9
|
+
return `${rstTargetName(vector)}:${normalizeServiceName(service)}`;
|
|
10
|
+
}
|
|
11
|
+
export function getRegisterCareProfile(name) {
|
|
12
|
+
if (name !== 'mon3')
|
|
13
|
+
return undefined;
|
|
14
|
+
return {
|
|
15
|
+
name: 'mon3',
|
|
16
|
+
rst: new Map([
|
|
17
|
+
[
|
|
18
|
+
0x10,
|
|
19
|
+
{
|
|
20
|
+
name: rstTargetName(0x10),
|
|
21
|
+
mayRead: [],
|
|
22
|
+
mayWrite: ['A', ...FLAG_UNITS],
|
|
23
|
+
preserved: ['B', 'C', 'D', 'E', 'H', 'L'],
|
|
24
|
+
valueRelations: [],
|
|
25
|
+
stackBalanced: true,
|
|
26
|
+
hasUnknownStackEffect: false,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
]),
|
|
30
|
+
rstServices: new Map([
|
|
31
|
+
[
|
|
32
|
+
rstServiceTargetName(0x10, 'API_SCANKEYS'),
|
|
33
|
+
{
|
|
34
|
+
name: rstServiceTargetName(0x10, 'API_SCANKEYS'),
|
|
35
|
+
mayRead: ['C'],
|
|
36
|
+
mayWrite: ['sign', 'parity', 'halfCarry'],
|
|
37
|
+
preserved: ['B', 'C', 'D', 'E', 'H', 'L'],
|
|
38
|
+
valueRelations: [{ out: ['A', 'carry', 'zero'], from: [] }],
|
|
39
|
+
stackBalanced: true,
|
|
40
|
+
hasUnknownStackEffect: false,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
]),
|
|
44
|
+
};
|
|
45
|
+
}
|