@jhlagado/azm 0.2.7 → 0.2.9
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/README.md +239 -76
- package/dist/src/api-artifacts.d.ts +20 -0
- package/dist/src/api-artifacts.js +165 -0
- package/dist/src/api-compile.d.ts +8 -2
- package/dist/src/api-compile.js +55 -227
- package/dist/src/api-register-contracts.d.ts +9 -0
- package/dist/src/api-register-contracts.js +77 -0
- package/dist/src/api-tooling.d.ts +2 -2
- package/dist/src/api-tooling.js +1 -1
- package/dist/src/assembly/address-planning.d.ts +1 -2
- package/dist/src/assembly/address-planning.js +119 -218
- package/dist/src/assembly/address-symbols.d.ts +12 -0
- package/dist/src/assembly/address-symbols.js +118 -0
- package/dist/src/assembly/assemble-program.js +5 -0
- package/dist/src/assembly/fixup-emission.js +30 -48
- package/dist/src/assembly/import-visibility.d.ts +3 -0
- package/dist/src/assembly/import-visibility.js +204 -0
- package/dist/src/assembly/program-emission.js +163 -164
- package/dist/src/cli/artifact-files.d.ts +15 -0
- package/dist/src/cli/artifact-files.js +86 -0
- package/dist/src/cli/parse-args.d.ts +6 -5
- package/dist/src/cli/parse-args.js +162 -136
- package/dist/src/cli/run.js +4 -1
- package/dist/src/cli/usage.d.ts +1 -0
- package/dist/src/cli/usage.js +33 -0
- package/dist/src/cli/write-artifacts.js +18 -91
- package/dist/src/core/compile.js +51 -274
- package/dist/src/core/conditional-assembly.d.ts +6 -0
- package/dist/src/core/conditional-assembly.js +181 -0
- package/dist/src/expansion/op-constant-expression.d.ts +3 -0
- package/dist/src/expansion/op-constant-expression.js +52 -0
- package/dist/src/expansion/op-expand-selected.d.ts +5 -0
- package/dist/src/expansion/op-expand-selected.js +143 -0
- package/dist/src/expansion/op-expansion.d.ts +5 -53
- package/dist/src/expansion/op-expansion.js +85 -815
- package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
- package/dist/src/expansion/op-instruction-instantiation.js +194 -0
- package/dist/src/expansion/op-local-labels.d.ts +8 -0
- package/dist/src/expansion/op-local-labels.js +166 -0
- package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
- package/dist/src/expansion/op-operand-splitting.js +44 -0
- package/dist/src/expansion/op-operands.d.ts +53 -0
- package/dist/src/expansion/op-operands.js +66 -0
- package/dist/src/expansion/op-selection.d.ts +18 -0
- package/dist/src/expansion/op-selection.js +172 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/model/diagnostic.d.ts +4 -0
- package/dist/src/model/diagnostic.js +4 -0
- package/dist/src/node/source-host.js +40 -13
- package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
- package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
- package/dist/src/outputs/asm80-expressions.d.ts +5 -0
- package/dist/src/outputs/asm80-expressions.js +47 -0
- package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
- package/dist/src/outputs/asm80-instruction-operands.js +38 -0
- package/dist/src/outputs/asm80-instructions.d.ts +5 -0
- package/dist/src/outputs/asm80-instructions.js +272 -0
- package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
- package/dist/src/outputs/asm80-ld-operands.js +157 -0
- package/dist/src/outputs/asm80-strings.d.ts +4 -0
- package/dist/src/outputs/asm80-strings.js +14 -0
- package/dist/src/outputs/d8-files.d.ts +10 -0
- package/dist/src/outputs/d8-files.js +103 -0
- package/dist/src/outputs/d8-helpers.d.ts +21 -0
- package/dist/src/outputs/d8-helpers.js +136 -0
- package/dist/src/outputs/hex.js +26 -18
- package/dist/src/outputs/types.d.ts +16 -10
- package/dist/src/outputs/write-asm80.js +72 -597
- package/dist/src/outputs/write-d8.js +6 -216
- package/dist/src/register-contracts/accept-output.d.ts +2 -0
- package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
- package/dist/src/register-contracts/analyze-helpers.js +162 -0
- package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
- package/dist/src/register-contracts/analyze.js +73 -0
- package/dist/src/register-contracts/annotate.d.ts +11 -0
- package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
- package/dist/src/register-contracts/annotations.d.ts +8 -0
- package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
- package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
- package/dist/src/register-contracts/boundaryHints.js +24 -0
- package/dist/src/register-contracts/carriers.d.ts +2 -0
- package/dist/src/register-contracts/constants.d.ts +4 -0
- package/dist/src/register-contracts/constants.js +51 -0
- package/dist/src/register-contracts/controlFlow.d.ts +5 -0
- package/dist/src/register-contracts/controlFlow.js +55 -0
- package/dist/src/register-contracts/fix.d.ts +11 -0
- package/dist/src/{register-care → register-contracts}/fix.js +47 -30
- package/dist/src/register-contracts/instruction-head.d.ts +2 -0
- package/dist/src/register-contracts/instruction-head.js +3 -0
- package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
- package/dist/src/register-contracts/instruction-operands.js +101 -0
- package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
- package/dist/src/register-contracts/instruction-predicates.js +44 -0
- package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
- package/dist/src/register-contracts/interfaceContracts.js +68 -0
- package/dist/src/register-contracts/liveness.d.ts +3 -0
- package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
- package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
- package/dist/src/register-contracts/operand-register-name.js +13 -0
- package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
- package/dist/src/{register-care → register-contracts}/profiles.js +2 -2
- package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
- package/dist/src/register-contracts/programModel-boundaries.js +64 -0
- package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
- package/dist/src/register-contracts/programModel-routines.js +144 -0
- package/dist/src/register-contracts/programModel.d.ts +3 -0
- package/dist/src/register-contracts/programModel.js +14 -0
- package/dist/src/register-contracts/report.d.ts +5 -0
- package/dist/src/{register-care → register-contracts}/report.js +34 -17
- package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
- package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
- package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
- package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
- package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
- package/dist/src/register-contracts/smartCommentParsing.js +80 -0
- package/dist/src/register-contracts/smartComments.d.ts +5 -0
- package/dist/src/register-contracts/smartComments.js +92 -0
- package/dist/src/register-contracts/summaries.d.ts +12 -0
- package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
- package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
- package/dist/src/register-contracts/summary-boundary.js +40 -0
- package/dist/src/register-contracts/summary-contract.d.ts +2 -0
- package/dist/src/register-contracts/summary-contract.js +45 -0
- package/dist/src/register-contracts/summary-result.d.ts +7 -0
- package/dist/src/register-contracts/summary-result.js +122 -0
- package/dist/src/register-contracts/summary-state.d.ts +23 -0
- package/dist/src/register-contracts/summary-state.js +88 -0
- package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
- package/dist/src/register-contracts/summary-token-transfer.js +67 -0
- package/dist/src/register-contracts/summary.d.ts +3 -0
- package/dist/src/register-contracts/summary.js +266 -0
- package/dist/src/register-contracts/tooling.d.ts +57 -0
- package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
- package/dist/src/register-contracts/types.d.ts +188 -0
- package/dist/src/semantics/binary-operators.d.ts +2 -0
- package/dist/src/semantics/binary-operators.js +15 -0
- package/dist/src/semantics/byte-functions.d.ts +2 -0
- package/dist/src/semantics/byte-functions.js +7 -0
- package/dist/src/semantics/constant-operator-types.d.ts +10 -0
- package/dist/src/semantics/constant-operator-types.js +1 -0
- package/dist/src/semantics/constant-operators.d.ts +3 -0
- package/dist/src/semantics/constant-operators.js +3 -0
- package/dist/src/semantics/diagnostics.d.ts +3 -0
- package/dist/src/semantics/diagnostics.js +10 -0
- package/dist/src/semantics/expression-evaluation.d.ts +11 -19
- package/dist/src/semantics/expression-evaluation.js +22 -334
- package/dist/src/semantics/layout-evaluation.d.ts +23 -0
- package/dist/src/semantics/layout-evaluation.js +202 -0
- package/dist/src/semantics/layout-format.d.ts +5 -0
- package/dist/src/semantics/layout-format.js +31 -0
- package/dist/src/semantics/layout-path.d.ts +24 -0
- package/dist/src/semantics/layout-path.js +58 -0
- package/dist/src/semantics/unary-operators.d.ts +2 -0
- package/dist/src/semantics/unary-operators.js +8 -0
- package/dist/src/source/line-comment-scanner.d.ts +1 -0
- package/dist/src/source/line-comment-scanner.js +51 -0
- package/dist/src/source/logical-lines.d.ts +3 -0
- package/dist/src/source/source-span.d.ts +2 -0
- package/dist/src/source/strip-line-comment.js +8 -44
- package/dist/src/syntax/directive-aliases.js +36 -22
- package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
- package/dist/src/syntax/expression-tokenizer.js +310 -0
- package/dist/src/syntax/parse-directive-statement.d.ts +9 -0
- package/dist/src/syntax/parse-directive-statement.js +309 -0
- package/dist/src/syntax/parse-expression.d.ts +2 -2
- package/dist/src/syntax/parse-expression.js +7 -568
- package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
- package/dist/src/syntax/parse-layout-declarations.js +189 -0
- package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
- package/dist/src/syntax/parse-layout-expression.js +175 -0
- package/dist/src/syntax/parse-line.js +21 -273
- package/dist/src/syntax/parse-token-expression.d.ts +3 -0
- package/dist/src/syntax/parse-token-expression.js +133 -0
- package/dist/src/tooling/api.js +1 -1
- package/dist/src/tooling/case-style.js +47 -30
- package/dist/src/z80/effect-groups.d.ts +38 -0
- package/dist/src/z80/effect-groups.js +265 -0
- package/dist/src/z80/effect-units.d.ts +18 -0
- package/dist/src/z80/effect-units.js +165 -0
- package/dist/src/z80/effects.d.ts +1 -1
- package/dist/src/z80/effects.js +94 -557
- package/dist/src/z80/encode-core.d.ts +2 -0
- package/dist/src/z80/encode-core.js +42 -0
- package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
- package/dist/src/z80/encode-ld-helpers.js +172 -0
- package/dist/src/z80/encode-ld.d.ts +2 -0
- package/dist/src/z80/encode-ld.js +285 -0
- package/dist/src/z80/encode.js +190 -542
- package/dist/src/z80/ld-support.d.ts +3 -0
- package/dist/src/z80/ld-support.js +146 -0
- package/dist/src/z80/operand-split-state.d.ts +8 -0
- package/dist/src/z80/operand-split-state.js +46 -0
- package/dist/src/z80/operand-split.d.ts +1 -0
- package/dist/src/z80/operand-split.js +13 -0
- package/dist/src/z80/parse-basic.d.ts +4 -0
- package/dist/src/z80/parse-basic.js +39 -0
- package/dist/src/z80/parse-branch.d.ts +4 -0
- package/dist/src/z80/parse-branch.js +218 -0
- package/dist/src/z80/parse-conditions.d.ts +6 -0
- package/dist/src/z80/parse-conditions.js +10 -0
- package/dist/src/z80/parse-exchange.d.ts +2 -0
- package/dist/src/z80/parse-exchange.js +30 -0
- package/dist/src/z80/parse-instruction.js +224 -1010
- package/dist/src/z80/parse-io-control.d.ts +5 -0
- package/dist/src/z80/parse-io-control.js +108 -0
- package/dist/src/z80/parse-ld.d.ts +2 -0
- package/dist/src/z80/parse-ld.js +83 -0
- package/dist/src/z80/parse-operands.d.ts +41 -0
- package/dist/src/z80/parse-operands.js +259 -0
- package/docs/codebase/01-orientation-and-repository-layout.md +192 -0
- package/docs/codebase/02-source-loading-and-parsing.md +263 -0
- package/docs/codebase/03-assembly-and-z80-emission.md +251 -0
- package/docs/codebase/04-ops-and-register-contracts.md +237 -0
- package/docs/codebase/05-interfaces-and-output-artifacts.md +253 -0
- package/docs/codebase/06-verification-and-maintenance.md +202 -0
- package/docs/codebase/appendices/a-directory-file-reference.md +253 -0
- package/docs/codebase/appendices/b-compile-flow-reference.md +103 -0
- package/docs/codebase/appendices/c-public-surface-reference.md +106 -0
- package/docs/codebase/appendices/index.md +16 -0
- package/docs/codebase/index.md +46 -0
- package/package.json +2 -3
- package/dist/src/register-care/accept-output.d.ts +0 -2
- package/dist/src/register-care/analyze.js +0 -166
- package/dist/src/register-care/annotate.d.ts +0 -11
- package/dist/src/register-care/annotations.d.ts +0 -8
- package/dist/src/register-care/boundaryHints.d.ts +0 -3
- package/dist/src/register-care/boundaryHints.js +0 -80
- package/dist/src/register-care/carriers.d.ts +0 -2
- package/dist/src/register-care/controlFlow.d.ts +0 -5
- package/dist/src/register-care/controlFlow.js +0 -38
- package/dist/src/register-care/fix.d.ts +0 -11
- package/dist/src/register-care/instruction-shape.d.ts +0 -11
- package/dist/src/register-care/instruction-shape.js +0 -129
- package/dist/src/register-care/liveness.d.ts +0 -3
- package/dist/src/register-care/programModel.d.ts +0 -3
- package/dist/src/register-care/programModel.js +0 -266
- package/dist/src/register-care/report.d.ts +0 -5
- package/dist/src/register-care/routine-summaries.d.ts +0 -6
- package/dist/src/register-care/smartComments.d.ts +0 -5
- package/dist/src/register-care/smartComments.js +0 -243
- package/dist/src/register-care/summaries.d.ts +0 -12
- package/dist/src/register-care/summary.d.ts +0 -3
- package/dist/src/register-care/summary.js +0 -474
- package/dist/src/register-care/tooling.d.ts +0 -43
- package/dist/src/register-care/types.d.ts +0 -172
- package/docs/reference/cli.md +0 -151
- package/docs/reference/tooling-api.md +0 -316
- /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
- /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
- /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
- /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
- /package/dist/src/{register-care → register-contracts}/types.js +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { instructionHead } from './instruction-head.js';
|
|
2
|
+
import { regName } from './operand-register-name.js';
|
|
3
|
+
import { instructionOperand, instructionOperandCount } from './instruction-operands.js';
|
|
4
|
+
import { getRegisterUnits, readToken, unique } from './summary-state.js';
|
|
5
|
+
export function applyPureTokenTransfer(tokens, consumedProduced, item) {
|
|
6
|
+
const head = instructionHead(item).toLowerCase();
|
|
7
|
+
if (head === 'ld')
|
|
8
|
+
return applyLdTokenTransfer(tokens, consumedProduced, item);
|
|
9
|
+
if (head === 'ex')
|
|
10
|
+
return applyExTokenTransfer(tokens, item);
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
function applyLdTokenTransfer(tokens, consumedProduced, item) {
|
|
14
|
+
if (instructionOperandCount(item.instruction) !== 2)
|
|
15
|
+
return [];
|
|
16
|
+
const dstUnits = operandRegisterUnits(item, 0);
|
|
17
|
+
if (!dstUnits)
|
|
18
|
+
return [];
|
|
19
|
+
const srcUnits = operandRegisterUnits(item, 1);
|
|
20
|
+
if (srcUnits && srcUnits.length === dstUnits.length) {
|
|
21
|
+
copyTokenUnits(tokens, consumedProduced, dstUnits, srcUnits);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
markUnitsProduced(tokens, consumedProduced, dstUnits);
|
|
25
|
+
}
|
|
26
|
+
return dstUnits;
|
|
27
|
+
}
|
|
28
|
+
function applyExTokenTransfer(tokens, item) {
|
|
29
|
+
if (instructionOperandCount(item.instruction) !== 2)
|
|
30
|
+
return [];
|
|
31
|
+
const leftUnits = operandRegisterUnits(item, 0);
|
|
32
|
+
const rightUnits = operandRegisterUnits(item, 1);
|
|
33
|
+
if (!leftUnits || !rightUnits || leftUnits.length !== rightUnits.length)
|
|
34
|
+
return [];
|
|
35
|
+
swapTokenUnits(tokens, leftUnits, rightUnits);
|
|
36
|
+
return unique([...leftUnits, ...rightUnits]);
|
|
37
|
+
}
|
|
38
|
+
function operandRegisterUnits(item, operandIndex) {
|
|
39
|
+
const name = regName(instructionOperand(item.instruction, operandIndex));
|
|
40
|
+
return name ? getRegisterUnits(name) : undefined;
|
|
41
|
+
}
|
|
42
|
+
function copyTokenUnits(tokens, consumedProduced, dstUnits, srcUnits) {
|
|
43
|
+
dstUnits.forEach((unit, index) => {
|
|
44
|
+
const sourceUnit = srcUnits[index];
|
|
45
|
+
const sourceToken = readToken(tokens, sourceUnit);
|
|
46
|
+
tokens.set(unit, sourceToken);
|
|
47
|
+
if (sourceToken.origin === 'produced')
|
|
48
|
+
consumedProduced.add(sourceUnit);
|
|
49
|
+
consumedProduced.delete(unit);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function markUnitsProduced(tokens, consumedProduced, units) {
|
|
53
|
+
for (const unit of units) {
|
|
54
|
+
tokens.set(unit, { origin: 'produced' });
|
|
55
|
+
consumedProduced.delete(unit);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function swapTokenUnits(tokens, leftUnits, rightUnits) {
|
|
59
|
+
const leftTokens = leftUnits.map((unit) => readToken(tokens, unit));
|
|
60
|
+
const rightTokens = rightUnits.map((unit) => readToken(tokens, unit));
|
|
61
|
+
leftUnits.forEach((unit, index) => {
|
|
62
|
+
tokens.set(unit, rightTokens[index] ?? { origin: 'unknown' });
|
|
63
|
+
});
|
|
64
|
+
rightUnits.forEach((unit, index) => {
|
|
65
|
+
tokens.set(unit, leftTokens[index] ?? { origin: 'unknown' });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { applyRoutineContract } from './summary-contract.js';
|
|
2
|
+
import type { RegisterContractsRoutine, RoutineSummary } from './types.js';
|
|
3
|
+
export declare function inferRoutineSummary(routine: RegisterContractsRoutine, boundarySummaries?: ReadonlyMap<string, RoutineSummary>): RoutineSummary;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { getZ80InstructionEffect } from '../z80/effects.js';
|
|
2
|
+
import { instructionHead } from './instruction-head.js';
|
|
3
|
+
import { isAccumulatorSelfOperand, isImmediateZeroOperand, isPureTokenTransferInstruction, isRegisterOperand, } from './instruction-predicates.js';
|
|
4
|
+
import { boundarySummary } from './summary-boundary.js';
|
|
5
|
+
import { CONTRACT_FLAG_UNITS, STACK_POINTER_UNITS, TRACKED_UNITS, isTrackedUnit, markProducedReadsConsumed, readToken, semanticReadOrigins, } from './summary-state.js';
|
|
6
|
+
import { applyPureTokenTransfer } from './summary-token-transfer.js';
|
|
7
|
+
export { applyRoutineContract } from './summary-contract.js';
|
|
8
|
+
import { buildRoutineSummary } from './summary-result.js';
|
|
9
|
+
function isOpaqueBoundary(item, effect) {
|
|
10
|
+
if (effect.control.kind === 'call' || effect.control.kind === 'rst')
|
|
11
|
+
return true;
|
|
12
|
+
return (effect.control.kind === 'jump' &&
|
|
13
|
+
(instructionHead(item) === 'jp' || instructionHead(item) === 'jp-cc') &&
|
|
14
|
+
!effect.control.conditional &&
|
|
15
|
+
Boolean(effect.control.target) &&
|
|
16
|
+
!effect.control.target?.startsWith('.'));
|
|
17
|
+
}
|
|
18
|
+
function isRoutineReturn(effect) {
|
|
19
|
+
return effect.control.kind === 'return';
|
|
20
|
+
}
|
|
21
|
+
function isPureTokenTransfer(item) {
|
|
22
|
+
return isPureTokenTransferInstruction(item);
|
|
23
|
+
}
|
|
24
|
+
function isCarryClearBeforeSbcHl(item, next) {
|
|
25
|
+
const head = instructionHead(item).toLowerCase();
|
|
26
|
+
if (head !== 'or' && head !== 'and')
|
|
27
|
+
return false;
|
|
28
|
+
if (!isAccumulatorSelfOperand(item))
|
|
29
|
+
return false;
|
|
30
|
+
return next !== undefined && instructionHead(next) === 'sbc' && isRegisterOperand(next, 0, 'HL');
|
|
31
|
+
}
|
|
32
|
+
function intentOutputUnits(item) {
|
|
33
|
+
const head = instructionHead(item).toLowerCase();
|
|
34
|
+
if (head === 'scf' || head === 'ccf')
|
|
35
|
+
return ['carry'];
|
|
36
|
+
if (head === 'cp')
|
|
37
|
+
return isImmediateZeroOperand(item) ? ['A', 'carry', 'zero'] : ['carry', 'zero'];
|
|
38
|
+
if ((head === 'or' || head === 'and' || head === 'xor') && isAccumulatorSelfOperand(item)) {
|
|
39
|
+
return ['A', 'carry', 'zero'];
|
|
40
|
+
}
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
function isMechanicalResidueWrite(item, unit) {
|
|
44
|
+
const head = instructionHead(item).toLowerCase();
|
|
45
|
+
if (head === 'djnz')
|
|
46
|
+
return unit === 'B';
|
|
47
|
+
if (BLOCK_TRANSFER_HEADS.has(head))
|
|
48
|
+
return BLOCK_TRANSFER_RESIDUE_UNITS.has(unit);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const BLOCK_TRANSFER_HEADS = new Set(['ldi', 'ldir', 'ldd', 'lddr']);
|
|
52
|
+
const BLOCK_TRANSFER_RESIDUE_UNITS = new Set(['B', 'C', 'D', 'E', 'H', 'L']);
|
|
53
|
+
function applyKnownBoundarySummary(tokens, consumedProduced, intendedProduced, directMayWrite, summary) {
|
|
54
|
+
for (const relation of summary.valueRelations) {
|
|
55
|
+
const sameCarrierRelation = relation.out.length === relation.from.length &&
|
|
56
|
+
relation.out.every((unit, index) => unit === relation.from[index]);
|
|
57
|
+
relation.out.forEach((unit, index) => {
|
|
58
|
+
if (!sameCarrierRelation &&
|
|
59
|
+
relation.from.length === relation.out.length &&
|
|
60
|
+
relation.from[index]) {
|
|
61
|
+
tokens.set(unit, readToken(tokens, relation.from[index]));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
tokens.set(unit, { origin: 'produced' });
|
|
65
|
+
consumedProduced.delete(unit);
|
|
66
|
+
if (CONTRACT_FLAG_UNITS.has(unit))
|
|
67
|
+
intendedProduced.add(unit);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
for (const unit of summary.mayWrite) {
|
|
72
|
+
if (STACK_POINTER_UNITS.has(unit))
|
|
73
|
+
continue;
|
|
74
|
+
if (isTrackedUnit(unit)) {
|
|
75
|
+
tokens.set(unit, { origin: 'unknown' });
|
|
76
|
+
consumedProduced.delete(unit);
|
|
77
|
+
intendedProduced.delete(unit);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
directMayWrite.push(unit);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function applyStackPop(tokens, consumedProduced, intendedProduced, stack, state, units) {
|
|
85
|
+
const popped = stack.pop();
|
|
86
|
+
if (!popped) {
|
|
87
|
+
state.stackBalanced = false;
|
|
88
|
+
for (const unit of units) {
|
|
89
|
+
tokens.set(unit, { origin: 'unknown' });
|
|
90
|
+
intendedProduced.delete(unit);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (popped.length !== units.length) {
|
|
95
|
+
for (const unit of units) {
|
|
96
|
+
tokens.set(unit, { origin: 'unknown' });
|
|
97
|
+
consumedProduced.delete(unit);
|
|
98
|
+
intendedProduced.delete(unit);
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
units.forEach((unit, idx) => {
|
|
103
|
+
tokens.set(unit, popped[idx] ?? { origin: 'unknown' });
|
|
104
|
+
consumedProduced.delete(unit);
|
|
105
|
+
intendedProduced.delete(unit);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function applyUnknownStackUnits(tokens, consumedProduced, intendedProduced, units) {
|
|
109
|
+
for (const unit of units) {
|
|
110
|
+
tokens.set(unit, { origin: 'unknown' });
|
|
111
|
+
consumedProduced.delete(unit);
|
|
112
|
+
intendedProduced.delete(unit);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function applyStackEffect(tokens, consumedProduced, intendedProduced, stack, state, effect, expectedTerminalReturn, knownBoundary) {
|
|
116
|
+
if (effect.stack.kind === 'push') {
|
|
117
|
+
stack.push(effect.stack.units.map((unit) => readToken(tokens, unit)));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (effect.stack.kind === 'pop') {
|
|
121
|
+
applyStackPop(tokens, consumedProduced, intendedProduced, stack, state, effect.stack.units);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (effect.stack.kind === 'exchangeTop') {
|
|
125
|
+
state.hasUnknownStackEffect = true;
|
|
126
|
+
applyUnknownStackUnits(tokens, consumedProduced, intendedProduced, effect.stack.units);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (expectedTerminalReturn && stack.length !== 0) {
|
|
130
|
+
state.stackBalanced = false;
|
|
131
|
+
}
|
|
132
|
+
if (effect.stack.kind === 'unknown' &&
|
|
133
|
+
!expectedTerminalReturn &&
|
|
134
|
+
(!knownBoundary || !knownBoundary.stackBalanced || knownBoundary.hasUnknownStackEffect)) {
|
|
135
|
+
state.hasUnknownStackEffect = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function applyEffectWrites(tokens, consumedProduced, intendedProduced, directMayWrite, item, effect, transferWrites, instructionIntentOutputs, carryClearBeforeSbcHl) {
|
|
139
|
+
for (const unit of effect.writes) {
|
|
140
|
+
if (shouldIgnoreEffectWrite(unit, effect, transferWrites))
|
|
141
|
+
continue;
|
|
142
|
+
if (applyAccumulatorSelfWrite(unit, item, intendedProduced, carryClearBeforeSbcHl))
|
|
143
|
+
continue;
|
|
144
|
+
applyEffectWriteUnit(tokens, consumedProduced, intendedProduced, directMayWrite, item, unit, instructionIntentOutputs);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function shouldIgnoreEffectWrite(unit, effect, transferWrites) {
|
|
148
|
+
return (STACK_POINTER_UNITS.has(unit) ||
|
|
149
|
+
isStackPopTrackedWrite(unit, effect) ||
|
|
150
|
+
(transferWrites.has(unit) && isTrackedUnit(unit)));
|
|
151
|
+
}
|
|
152
|
+
function isStackPopTrackedWrite(unit, effect) {
|
|
153
|
+
return effect.stack.kind === 'pop' && effect.stack.units.includes(unit) && isTrackedUnit(unit);
|
|
154
|
+
}
|
|
155
|
+
function applyAccumulatorSelfWrite(unit, item, intendedProduced, carryClearBeforeSbcHl) {
|
|
156
|
+
if (unit !== 'A' || !isOrAndAccumulatorSelf(item))
|
|
157
|
+
return false;
|
|
158
|
+
if (!carryClearBeforeSbcHl)
|
|
159
|
+
intendedProduced.add(unit);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
function isOrAndAccumulatorSelf(item) {
|
|
163
|
+
const head = instructionHead(item).toLowerCase();
|
|
164
|
+
return (head === 'or' || head === 'and') && isAccumulatorSelfOperand(item);
|
|
165
|
+
}
|
|
166
|
+
function applyEffectWriteUnit(tokens, consumedProduced, intendedProduced, directMayWrite, item, unit, instructionIntentOutputs) {
|
|
167
|
+
if (!isTrackedUnit(unit)) {
|
|
168
|
+
directMayWrite.push(unit);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
tokens.set(unit, { origin: isMechanicalResidueWrite(item, unit) ? 'unknown' : 'produced' });
|
|
172
|
+
consumedProduced.delete(unit);
|
|
173
|
+
if (instructionIntentOutputs.includes(unit))
|
|
174
|
+
intendedProduced.add(unit);
|
|
175
|
+
else
|
|
176
|
+
intendedProduced.delete(unit);
|
|
177
|
+
}
|
|
178
|
+
function addInstructionIntentOutputs(intendedProduced, effectWrites, instructionIntentOutputs) {
|
|
179
|
+
for (const unit of instructionIntentOutputs) {
|
|
180
|
+
if (!isTrackedUnit(unit) || effectWrites.has(unit))
|
|
181
|
+
continue;
|
|
182
|
+
intendedProduced.add(unit);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function recordInstructionReads(tokens, consumedProduced, intendedProduced, mayRead, item, effect, knownBoundary, semanticReads, effectWrites) {
|
|
186
|
+
if (effect.stack.kind !== 'push' && !isPureTokenTransfer(item)) {
|
|
187
|
+
mayRead.push(...semanticReadOrigins(tokens, semanticReads));
|
|
188
|
+
markProducedReadsConsumed(tokens, consumedProduced, semanticReads, effectWrites, item);
|
|
189
|
+
}
|
|
190
|
+
if (instructionHead(item).toLowerCase() === 'djnz') {
|
|
191
|
+
for (const unit of TRACKED_UNITS) {
|
|
192
|
+
if (readToken(tokens, unit).origin === 'produced')
|
|
193
|
+
consumedProduced.add(unit);
|
|
194
|
+
intendedProduced.delete(unit);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (knownBoundary) {
|
|
198
|
+
mayRead.push(...semanticReadOrigins(tokens, knownBoundary.mayRead));
|
|
199
|
+
markProducedReadsConsumed(tokens, consumedProduced, knownBoundary.mayRead, new Set());
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function createInferenceState() {
|
|
203
|
+
const tokens = new Map();
|
|
204
|
+
for (const unit of TRACKED_UNITS)
|
|
205
|
+
tokens.set(unit, { origin: unit });
|
|
206
|
+
return {
|
|
207
|
+
tokens,
|
|
208
|
+
stack: [],
|
|
209
|
+
mayRead: [],
|
|
210
|
+
directMayWrite: [],
|
|
211
|
+
consumedProduced: new Set(),
|
|
212
|
+
intendedProduced: new Set(),
|
|
213
|
+
stackState: {
|
|
214
|
+
stackBalanced: true,
|
|
215
|
+
hasUnknownStackEffect: false,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function instructionInferenceContext(routine, index, boundarySummaries) {
|
|
220
|
+
const item = routine.instructions[index];
|
|
221
|
+
const effect = getZ80InstructionEffect(item.instruction);
|
|
222
|
+
const carryClearBeforeSbcHl = isCarryClearBeforeSbcHl(item, routine.instructions[index + 1]);
|
|
223
|
+
return {
|
|
224
|
+
item,
|
|
225
|
+
effect,
|
|
226
|
+
knownBoundary: boundarySummary(routine, index, boundarySummaries),
|
|
227
|
+
carryClearBeforeSbcHl,
|
|
228
|
+
expectedTerminalReturn: isRoutineReturn(effect),
|
|
229
|
+
effectWrites: new Set(effect.writes),
|
|
230
|
+
instructionIntentOutputs: carryClearBeforeSbcHl ? [] : intentOutputUnits(item),
|
|
231
|
+
semanticReads: carryClearBeforeSbcHl
|
|
232
|
+
? effect.reads.filter((unit) => unit !== 'A')
|
|
233
|
+
: effect.reads,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function applyBoundaryOrOpaqueWrites(state, context) {
|
|
237
|
+
if (context.knownBoundary) {
|
|
238
|
+
applyKnownBoundarySummary(state.tokens, state.consumedProduced, state.intendedProduced, state.directMayWrite, context.knownBoundary);
|
|
239
|
+
}
|
|
240
|
+
else if (isOpaqueBoundary(context.item, context.effect)) {
|
|
241
|
+
for (const unit of TRACKED_UNITS) {
|
|
242
|
+
state.tokens.set(unit, { origin: 'unknown' });
|
|
243
|
+
state.consumedProduced.delete(unit);
|
|
244
|
+
state.intendedProduced.delete(unit);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function inferInstructionSummaryStep(state, context) {
|
|
249
|
+
recordInstructionReads(state.tokens, state.consumedProduced, state.intendedProduced, state.mayRead, context.item, context.effect, context.knownBoundary, [...context.semanticReads], context.effectWrites);
|
|
250
|
+
applyStackEffect(state.tokens, state.consumedProduced, state.intendedProduced, state.stack, state.stackState, context.effect, context.expectedTerminalReturn, context.knownBoundary);
|
|
251
|
+
const transferWrites = new Set(isPureTokenTransfer(context.item)
|
|
252
|
+
? applyPureTokenTransfer(state.tokens, state.consumedProduced, context.item)
|
|
253
|
+
: []);
|
|
254
|
+
applyBoundaryOrOpaqueWrites(state, context);
|
|
255
|
+
applyEffectWrites(state.tokens, state.consumedProduced, state.intendedProduced, state.directMayWrite, context.item, context.effect, transferWrites, context.instructionIntentOutputs, context.carryClearBeforeSbcHl);
|
|
256
|
+
addInstructionIntentOutputs(state.intendedProduced, context.effectWrites, context.instructionIntentOutputs);
|
|
257
|
+
}
|
|
258
|
+
export function inferRoutineSummary(routine, boundarySummaries = new Map()) {
|
|
259
|
+
const state = createInferenceState();
|
|
260
|
+
for (let index = 0; index < routine.instructions.length; index += 1) {
|
|
261
|
+
inferInstructionSummaryStep(state, instructionInferenceContext(routine, index, boundarySummaries));
|
|
262
|
+
}
|
|
263
|
+
if (state.stack.length !== 0)
|
|
264
|
+
state.stackState.stackBalanced = false;
|
|
265
|
+
return buildRoutineSummary(routine, state.tokens, state.consumedProduced, state.intendedProduced, state.directMayWrite, state.mayRead, state.stackState);
|
|
266
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Diagnostic } from '../model/diagnostic.js';
|
|
2
|
+
import type { LoadedProgram } from '../tooling/api.js';
|
|
3
|
+
import type { AnalyzeRegisterContractsOptions, RegisterContractsOutputCandidate, RegisterContractsUnit } from './types.js';
|
|
4
|
+
export interface RegisterContractsTextEdit {
|
|
5
|
+
file: string;
|
|
6
|
+
line: number;
|
|
7
|
+
column: number;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
/** @deprecated Use RegisterContractsTextEdit. */
|
|
11
|
+
export type RegisterCareTextEdit = RegisterContractsTextEdit;
|
|
12
|
+
export interface RegisterContractsCodeAction {
|
|
13
|
+
title: string;
|
|
14
|
+
kind: 'quickfix';
|
|
15
|
+
edit: RegisterContractsTextEdit;
|
|
16
|
+
}
|
|
17
|
+
/** @deprecated Use RegisterContractsCodeAction. */
|
|
18
|
+
export type RegisterCareCodeAction = RegisterContractsCodeAction;
|
|
19
|
+
export interface RegisterContractsCandidateDiagnostic {
|
|
20
|
+
kind: 'register-contracts-output-candidate';
|
|
21
|
+
severity: 'info';
|
|
22
|
+
file: string;
|
|
23
|
+
line: number;
|
|
24
|
+
column: number;
|
|
25
|
+
routine: string;
|
|
26
|
+
carriers: RegisterContractsUnit[];
|
|
27
|
+
autoFixable: boolean;
|
|
28
|
+
message: string;
|
|
29
|
+
codeAction?: RegisterContractsCodeAction;
|
|
30
|
+
}
|
|
31
|
+
/** @deprecated Use RegisterContractsCandidateDiagnostic. */
|
|
32
|
+
export type RegisterCareCandidateDiagnostic = RegisterContractsCandidateDiagnostic;
|
|
33
|
+
export interface AnalyzeRegisterContractsForToolsOptions extends Omit<AnalyzeRegisterContractsOptions, 'emitReport' | 'emitInterface' | 'emitAnnotations' | 'fixRegisterContracts'> {
|
|
34
|
+
emitReport?: boolean;
|
|
35
|
+
emitInterface?: boolean;
|
|
36
|
+
/** Back-compat alias for registerContractsProfile used by some tooling integrations. */
|
|
37
|
+
profile?: AnalyzeRegisterContractsOptions['registerContractsProfile'];
|
|
38
|
+
/** @deprecated Use registerContractsProfile. */
|
|
39
|
+
registerCareProfile?: AnalyzeRegisterContractsOptions['registerContractsProfile'];
|
|
40
|
+
}
|
|
41
|
+
/** @deprecated Use AnalyzeRegisterContractsForToolsOptions. */
|
|
42
|
+
export type AnalyzeRegisterCareForToolsOptions = AnalyzeRegisterContractsForToolsOptions;
|
|
43
|
+
export interface AnalyzeRegisterContractsForToolsResult {
|
|
44
|
+
diagnostics: Diagnostic[];
|
|
45
|
+
outputCandidates: RegisterContractsOutputCandidate[];
|
|
46
|
+
candidateDiagnostics: RegisterContractsCandidateDiagnostic[];
|
|
47
|
+
codeActions: RegisterContractsCodeAction[];
|
|
48
|
+
reportText?: string;
|
|
49
|
+
interfaceText?: string;
|
|
50
|
+
}
|
|
51
|
+
/** @deprecated Use AnalyzeRegisterContractsForToolsResult. */
|
|
52
|
+
export type AnalyzeRegisterCareForToolsResult = AnalyzeRegisterContractsForToolsResult;
|
|
53
|
+
export declare function codeActionForOutputCandidate(candidate: RegisterContractsOutputCandidate): RegisterContractsCodeAction;
|
|
54
|
+
export declare function diagnosticForOutputCandidate(candidate: RegisterContractsOutputCandidate): RegisterContractsCandidateDiagnostic;
|
|
55
|
+
export declare function analyzeRegisterContractsForTools(loaded: LoadedProgram, options: AnalyzeRegisterContractsForToolsOptions): AnalyzeRegisterContractsForToolsResult;
|
|
56
|
+
/** @deprecated Use analyzeRegisterContractsForTools. */
|
|
57
|
+
export declare const analyzeRegisterCareForTools: typeof analyzeRegisterContractsForTools;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { analyzeRegisterContracts } from './analyze.js';
|
|
2
2
|
import { contractCarrierList } from './report.js';
|
|
3
3
|
function expectOutText(carriers) {
|
|
4
4
|
return `; expects out ${contractCarrierList(carriers)}\n`;
|
|
@@ -18,7 +18,7 @@ export function codeActionForOutputCandidate(candidate) {
|
|
|
18
18
|
export function diagnosticForOutputCandidate(candidate) {
|
|
19
19
|
const codeAction = candidate.autoFixable === true ? codeActionForOutputCandidate(candidate) : undefined;
|
|
20
20
|
const diagnostic = {
|
|
21
|
-
kind: 'register-
|
|
21
|
+
kind: 'register-contracts-output-candidate',
|
|
22
22
|
severity: 'info',
|
|
23
23
|
file: candidate.file,
|
|
24
24
|
line: candidate.line,
|
|
@@ -33,8 +33,8 @@ export function diagnosticForOutputCandidate(candidate) {
|
|
|
33
33
|
}
|
|
34
34
|
return diagnostic;
|
|
35
35
|
}
|
|
36
|
-
export function
|
|
37
|
-
const profile = options.registerCareProfile ?? options.profile;
|
|
36
|
+
export function analyzeRegisterContractsForTools(loaded, options) {
|
|
37
|
+
const profile = options.registerContractsProfile ?? options.registerCareProfile ?? options.profile;
|
|
38
38
|
const baseResultOptions = {
|
|
39
39
|
...options,
|
|
40
40
|
emitReport: options.emitReport === true,
|
|
@@ -42,9 +42,9 @@ export function analyzeRegisterCareForTools(loaded, options) {
|
|
|
42
42
|
emitAnnotations: false,
|
|
43
43
|
fixRegisterContracts: false,
|
|
44
44
|
};
|
|
45
|
-
const result =
|
|
45
|
+
const result = analyzeRegisterContracts(loaded, profile === undefined
|
|
46
46
|
? baseResultOptions
|
|
47
|
-
: { ...baseResultOptions,
|
|
47
|
+
: { ...baseResultOptions, registerContractsProfile: profile });
|
|
48
48
|
const outputCandidates = result.outputCandidates ?? [];
|
|
49
49
|
const candidateDiagnostics = outputCandidates.map(diagnosticForOutputCandidate);
|
|
50
50
|
const codeActions = outputCandidates
|
|
@@ -59,3 +59,5 @@ export function analyzeRegisterCareForTools(loaded, options) {
|
|
|
59
59
|
...(result.interfaceText !== undefined ? { interfaceText: result.interfaceText } : {}),
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
+
/** @deprecated Use analyzeRegisterContractsForTools. */
|
|
63
|
+
export const analyzeRegisterCareForTools = analyzeRegisterContractsForTools;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { Z80Instruction } from '../z80/instruction.js';
|
|
2
|
+
export type RegisterContractsMode = 'off' | 'audit' | 'warn' | 'error' | 'strict';
|
|
3
|
+
/** @deprecated Use RegisterContractsMode. */
|
|
4
|
+
export type RegisterCareMode = RegisterContractsMode;
|
|
5
|
+
export type RegisterContractsUnit = 'A' | 'B' | 'C' | 'D' | 'E' | 'H' | 'L' | 'IXH' | 'IXL' | 'IYH' | 'IYL' | 'SPH' | 'SPL' | 'carry' | 'zero' | 'sign' | 'parity' | 'halfCarry';
|
|
6
|
+
/** @deprecated Use RegisterContractsUnit. */
|
|
7
|
+
export type RegisterCareUnit = RegisterContractsUnit;
|
|
8
|
+
export type SmartComment = {
|
|
9
|
+
kind: 'extern';
|
|
10
|
+
name: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'end';
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'in';
|
|
15
|
+
carriers: RegisterContractsUnit[];
|
|
16
|
+
name?: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'out';
|
|
19
|
+
carriers: RegisterContractsUnit[];
|
|
20
|
+
name?: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'clobbers';
|
|
23
|
+
carriers: RegisterContractsUnit[];
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'preserves';
|
|
26
|
+
carriers: RegisterContractsUnit[];
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'expectOut';
|
|
29
|
+
carriers: RegisterContractsUnit[];
|
|
30
|
+
name?: string;
|
|
31
|
+
};
|
|
32
|
+
export interface LocatedSmartComment {
|
|
33
|
+
file: string;
|
|
34
|
+
line: number;
|
|
35
|
+
comment: SmartComment;
|
|
36
|
+
}
|
|
37
|
+
export interface RoutineContract {
|
|
38
|
+
name: string;
|
|
39
|
+
in: RegisterContractsUnit[];
|
|
40
|
+
out: RegisterContractsUnit[];
|
|
41
|
+
clobbers: RegisterContractsUnit[];
|
|
42
|
+
preserves: RegisterContractsUnit[];
|
|
43
|
+
complete?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface RegisterContractsInstruction {
|
|
46
|
+
instruction: Z80Instruction;
|
|
47
|
+
file: string;
|
|
48
|
+
line: number;
|
|
49
|
+
column: number;
|
|
50
|
+
labels: string[];
|
|
51
|
+
constants?: ReadonlyMap<string, number>;
|
|
52
|
+
}
|
|
53
|
+
/** @deprecated Use RegisterContractsInstruction. */
|
|
54
|
+
export type RegisterCareInstruction = RegisterContractsInstruction;
|
|
55
|
+
export interface RegisterContractsRoutine {
|
|
56
|
+
name: string;
|
|
57
|
+
labels: string[];
|
|
58
|
+
entryLabels: string[];
|
|
59
|
+
instructions: RegisterContractsInstruction[];
|
|
60
|
+
constants?: ReadonlyMap<string, number>;
|
|
61
|
+
span: {
|
|
62
|
+
file: string;
|
|
63
|
+
start: {
|
|
64
|
+
line: number;
|
|
65
|
+
column: number;
|
|
66
|
+
};
|
|
67
|
+
end: {
|
|
68
|
+
line: number;
|
|
69
|
+
column: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** @deprecated Use RegisterContractsRoutine. */
|
|
74
|
+
export type RegisterCareRoutine = RegisterContractsRoutine;
|
|
75
|
+
export interface RegisterContractsDirectCall {
|
|
76
|
+
target: string;
|
|
77
|
+
subject: string;
|
|
78
|
+
file: string;
|
|
79
|
+
line: number;
|
|
80
|
+
column: number;
|
|
81
|
+
}
|
|
82
|
+
/** @deprecated Use RegisterContractsDirectCall. */
|
|
83
|
+
export type RegisterCareDirectCall = RegisterContractsDirectCall;
|
|
84
|
+
export interface RegisterContractsProgramModel {
|
|
85
|
+
routines: RegisterContractsRoutine[];
|
|
86
|
+
directCalls: RegisterContractsDirectCall[];
|
|
87
|
+
directBoundaries: RegisterContractsDirectCall[];
|
|
88
|
+
}
|
|
89
|
+
/** @deprecated Use RegisterContractsProgramModel. */
|
|
90
|
+
export type RegisterCareProgramModel = RegisterContractsProgramModel;
|
|
91
|
+
export type StackEffect = {
|
|
92
|
+
kind: 'none';
|
|
93
|
+
} | {
|
|
94
|
+
kind: 'push';
|
|
95
|
+
units: RegisterContractsUnit[];
|
|
96
|
+
} | {
|
|
97
|
+
kind: 'pop';
|
|
98
|
+
units: RegisterContractsUnit[];
|
|
99
|
+
} | {
|
|
100
|
+
kind: 'exchangeTop';
|
|
101
|
+
units: RegisterContractsUnit[];
|
|
102
|
+
} | {
|
|
103
|
+
kind: 'unknown';
|
|
104
|
+
};
|
|
105
|
+
export type ControlEffect = {
|
|
106
|
+
kind: 'fallthrough';
|
|
107
|
+
} | {
|
|
108
|
+
kind: 'call';
|
|
109
|
+
target?: string;
|
|
110
|
+
conditional: boolean;
|
|
111
|
+
} | {
|
|
112
|
+
kind: 'rst';
|
|
113
|
+
vector?: number;
|
|
114
|
+
} | {
|
|
115
|
+
kind: 'return';
|
|
116
|
+
conditional: boolean;
|
|
117
|
+
} | {
|
|
118
|
+
kind: 'jump';
|
|
119
|
+
target?: string;
|
|
120
|
+
conditional: boolean;
|
|
121
|
+
} | {
|
|
122
|
+
kind: 'unknown';
|
|
123
|
+
};
|
|
124
|
+
export interface InstructionEffect {
|
|
125
|
+
reads: RegisterContractsUnit[];
|
|
126
|
+
writes: RegisterContractsUnit[];
|
|
127
|
+
stack: StackEffect;
|
|
128
|
+
control: ControlEffect;
|
|
129
|
+
}
|
|
130
|
+
export interface ValueRelation {
|
|
131
|
+
out: RegisterContractsUnit[];
|
|
132
|
+
from: RegisterContractsUnit[];
|
|
133
|
+
}
|
|
134
|
+
export interface RoutineSummary {
|
|
135
|
+
name: string;
|
|
136
|
+
mayRead: RegisterContractsUnit[];
|
|
137
|
+
mayWrite: RegisterContractsUnit[];
|
|
138
|
+
mayOutput?: RegisterContractsUnit[];
|
|
139
|
+
preserved: RegisterContractsUnit[];
|
|
140
|
+
valueRelations: ValueRelation[];
|
|
141
|
+
stackBalanced: boolean;
|
|
142
|
+
hasUnknownStackEffect?: boolean;
|
|
143
|
+
outputCandidates?: RegisterContractsUnit[];
|
|
144
|
+
}
|
|
145
|
+
export interface RegisterContractsOutputCandidate {
|
|
146
|
+
file: string;
|
|
147
|
+
line: number;
|
|
148
|
+
column: number;
|
|
149
|
+
routine: string;
|
|
150
|
+
carriers: RegisterContractsUnit[];
|
|
151
|
+
autoFixable?: boolean;
|
|
152
|
+
message: string;
|
|
153
|
+
}
|
|
154
|
+
/** @deprecated Use RegisterContractsOutputCandidate. */
|
|
155
|
+
export type RegisterCareOutputCandidate = RegisterContractsOutputCandidate;
|
|
156
|
+
export interface RegisterContractsConflict {
|
|
157
|
+
file: string;
|
|
158
|
+
line: number;
|
|
159
|
+
column: number;
|
|
160
|
+
callTarget: string;
|
|
161
|
+
carriers: RegisterContractsUnit[];
|
|
162
|
+
message: string;
|
|
163
|
+
}
|
|
164
|
+
/** @deprecated Use RegisterContractsConflict. */
|
|
165
|
+
export type RegisterCareConflict = RegisterContractsConflict;
|
|
166
|
+
export interface RegisterContractsReportModel {
|
|
167
|
+
entryFile: string;
|
|
168
|
+
mode: RegisterContractsMode;
|
|
169
|
+
profile?: string;
|
|
170
|
+
summaries: RoutineSummary[];
|
|
171
|
+
conflicts: RegisterContractsConflict[];
|
|
172
|
+
outputCandidates?: RegisterContractsOutputCandidate[];
|
|
173
|
+
unknownCalls: string[];
|
|
174
|
+
}
|
|
175
|
+
export interface AnalyzeRegisterContractsOptions {
|
|
176
|
+
mode: RegisterContractsMode;
|
|
177
|
+
emitReport: boolean;
|
|
178
|
+
emitInterface: boolean;
|
|
179
|
+
emitAnnotations?: boolean;
|
|
180
|
+
fixRegisterContracts?: boolean;
|
|
181
|
+
registerContractsProfile?: 'mon3';
|
|
182
|
+
interfaceContracts?: RoutineContract[];
|
|
183
|
+
acceptedOutputCandidates?: ReadonlyMap<string, RegisterContractsUnit[]>;
|
|
184
|
+
}
|
|
185
|
+
export interface RegisterContractsAnnotationFile {
|
|
186
|
+
readonly path: string;
|
|
187
|
+
readonly text: string;
|
|
188
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const binaryOperators = {
|
|
2
|
+
'+': (left, right) => left + right,
|
|
3
|
+
'-': (left, right) => left - right,
|
|
4
|
+
'*': (left, right) => left * right,
|
|
5
|
+
'/': (left, right) => (right === 0 ? undefined : Math.trunc(left / right)),
|
|
6
|
+
'%': (left, right) => (right === 0 ? undefined : left % right),
|
|
7
|
+
'&': (left, right) => left & right,
|
|
8
|
+
'^': (left, right) => left ^ right,
|
|
9
|
+
'|': (left, right) => left | right,
|
|
10
|
+
'<<': (left, right) => left << right,
|
|
11
|
+
'>>': (left, right) => left >> right,
|
|
12
|
+
};
|
|
13
|
+
export function applyBinaryOperator(operator, left, right) {
|
|
14
|
+
return binaryOperators[operator](left, right);
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Expression } from '../model/expression.js';
|
|
2
|
+
export type UnaryOperator = Extract<Expression, {
|
|
3
|
+
readonly kind: 'unary';
|
|
4
|
+
}>['operator'];
|
|
5
|
+
export type BinaryOperator = Extract<Expression, {
|
|
6
|
+
readonly kind: 'binary';
|
|
7
|
+
}>['operator'];
|
|
8
|
+
export type ByteFunction = Extract<Expression, {
|
|
9
|
+
readonly kind: 'byte-function';
|
|
10
|
+
}>['function'];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|