Files
reverse-engineering-anything/experiments/atomic-codemode/native-ir.mjs
T
f9f9dc209d feat(experiment): retain call facts beyond full graph capacity
Separate native call and boundary-value ownership from the bounded recovered graph. Validate facets independently and preserve shared identities, source evidence, and explicit partial coverage. Pin the SQLite boundary task and oracle before paired real-model trials.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 09:46:42 +00:00

406 lines
13 KiB
JavaScript

// The native-format boundary owns validation and indexes; consumers never parse
// diagnostic statements to recover value identity, operand indexes or CFG edges.
import { FactFailure } from "./fact-error.mjs";
import { parseNativeFunction } from "./native-ir-contract.mjs";
import { backwardSlice } from "../atomic-re-tools/kuna-ssa.mjs";
import { controlDependencies } from "./control-dependence.mjs";
import { freezeFacts as freeze } from "./fact-reads.mjs";
import {
indexCallFacet,
validateCallGraph,
createCallViews,
} from "./native-call-views.mjs";
import { requireFact } from "./native-contract.mjs";
import {
uniqueNative as unique,
nativePage as page,
readNativeIdentities as readIdentities,
} from "./native-reads.mjs";
const index = (map, key, row) => {
if (!map.has(key)) map.set(key, []);
map.get(key).push(row);
};
const kind = (opcode) =>
({
CPUI_LOAD: "load",
CPUI_STORE: "store",
CPUI_MULTIEQUAL: "phi",
CPUI_CALL: "call",
CPUI_CALLIND: "call",
CPUI_BRANCH: "branch",
CPUI_CBRANCH: "branch",
CPUI_BRANCHIND: "switch",
CPUI_RETURN: "return",
})[opcode] ?? "value";
function validateBlocks(operations, values, blocks) {
const membership = new Set();
for (const block of blocks.values()) {
for (const id of block.operations) {
requireFact(
operations.get(id)?.block === block.id && !membership.has(id),
"Native operation/block ownership mismatch",
);
membership.add(id);
}
for (const [index, edge] of block.successors.entries()) {
requireFact(
edge.index === index &&
edge.id === `${block.id}:${index}` &&
blocks.get(edge.target)?.predecessors.includes(block.id),
"Native CFG successor/predecessor mismatch",
);
requireFact(
edge.condition_value === null || values.has(edge.condition_value),
"Dangling native branch condition",
);
}
for (const from of block.predecessors)
requireFact(
blocks.get(from)?.successors.some((edge) => edge.target === block.id),
"Dangling native predecessor",
);
}
requireFact(
membership.size === operations.size,
"Native operation omitted from block membership",
);
}
function validateOperations(operations, values, blocks) {
for (const op of operations.values()) {
for (const [index, input] of op.inputs.entries()) {
requireFact(
input.input_index === index &&
(input.value_id === null || values.has(input.value_id)),
"Dangling or misindexed native operand",
);
requireFact(
input.phi_predecessor === null ||
(op.opcode === "CPUI_MULTIEQUAL" &&
blocks.get(op.block).predecessors[index] === input.phi_predecessor),
"Native phi/predecessor mismatch",
);
if (input.phi_predecessor_edge !== null)
requireFact(
input.phi_predecessor !== null &&
blocks
.get(input.phi_predecessor)
?.successors.some(
(edge) =>
edge.id === input.phi_predecessor_edge &&
edge.target === op.block,
),
"Native phi incoming-edge identity mismatch",
);
}
requireFact(
op.output === null || values.get(op.output)?.definition === op.id,
"Native output/definition mismatch",
);
}
}
function validateValues(facts, operations, values) {
const omitted = new Set(facts.coverage.omitted_definition_ids);
for (const value of values.values())
if (value.definition !== null)
requireFact(
operations.get(value.definition)?.output === value.id ||
(!operations.has(value.definition) && omitted.has(value.definition)),
"Native value/definition mismatch",
);
}
function indexNative(facts) {
const operations = unique(facts.operations),
values = unique(facts.values),
blocks = unique(
facts.blocks.map(({ start, ...block }) =>
freeze({ ...block, origin_start: start }),
),
);
validateBlocks(operations, values, blocks);
validateOperations(operations, values, blocks);
validateValues(facts, operations, values);
const users = new Map(),
incoming = new Map(),
outgoing = new Map(),
constants = new Map();
const normalized = new Map();
for (const op of operations.values()) {
const definitions = [],
literals = [];
for (const input of op.inputs) {
const value = values.get(input.value_id);
if (value?.definition) {
const relation = freeze({
definition: value.definition,
use: op.id,
input_index: input.input_index,
value_id: value.id,
phi_predecessor: input.phi_predecessor,
phi_predecessor_edge: input.phi_predecessor_edge,
});
definitions.push(relation);
index(users, value.definition, relation);
}
if (value?.is_constant) {
literals.push({
value: value.offset,
printed_size_bytes: value.size_bytes,
is_annotation: value.is_annotation,
});
index(constants, value.offset, op.id);
}
}
const { address: origin_address, ...payload } = op;
normalized.set(
op.id,
freeze({
...payload,
origin_address,
kind: kind(op.opcode),
source_line: null,
definitions,
constants: literals,
}),
);
}
for (const block of blocks.values())
for (const edge of block.successors) {
const row = freeze({
...edge,
from: block.id,
to: edge.target,
operation: block.operations.at(-1) ?? null,
kind: edge.polarity ? "conditional" : "recovered",
});
index(incoming, edge.target, row);
index(outgoing, block.id, row);
}
return {
operations,
values,
blocks,
users,
incoming,
outgoing,
constants,
normalized,
};
}
function createViews(facts, code, indexes) {
const { values, blocks, users, incoming, outgoing, constants, normalized } =
indexes;
const coverage = freeze({
...facts.coverage,
operations: normalized.size,
blocks: blocks.size,
control_flow: "complete-for-recovered-graph",
representation:
"optimized recovered IR; origins do not establish machine branch targets or block extents",
runtime_successors: "unknown",
memory_alias_dependencies: "unknown",
issues: facts.coverage.issues,
});
const model = {
code,
coverage,
operation: (id) => normalized.get(id) ?? null,
block: (id) => blocks.get(id) ?? null,
findConstant: (value) => [...new Set(constants.get(value) ?? [])],
uses: (id) => users.get(id) ?? [],
incoming: (id) => incoming.get(id) ?? [],
select: (input) =>
selectOperations(normalized, constants, {
fields: ["kind", "statement", "definitions", "constants"],
...input,
}),
values: (input) => ({
...readIdentities(
values,
input,
(value) =>
input.is_input === undefined || value.is_input === input.is_input,
),
coverage,
}),
blocks: (input) => readIdentities(blocks, input),
edges: (input) => {
if (input.kind === "controllers") {
const controls = controlDependencies(facts.blocks, {
dependents: new Set(input.seeds),
});
return {
...page(controls.relations, input),
relation_coverage: controls.coverage,
};
}
return page(
input.seeds.flatMap((seed) =>
input.kind === "definitions"
? (normalized.get(seed)?.definitions ?? [])
: ((input.kind === "uses"
? users
: input.kind === "incoming"
? incoming
: outgoing
).get(seed) ?? []),
),
input,
);
},
slice: (input) => sliceValues(model, values, input),
};
return Object.freeze(model);
}
function selectOperations(normalized, constants, input = {}) {
const ids = input.ids && new Set(input.ids),
addresses = input.origin_addresses && new Set(input.origin_addresses),
blockIds = input.block_ids && new Set(input.block_ids);
const candidates = ids
? [...ids].flatMap((id) => normalized.get(id) ?? [])
: input.constant
? [...new Set(constants.get(input.constant) ?? [])].map((id) =>
normalized.get(id),
)
: normalized.values();
return page(
[...candidates].filter(
(op) =>
(!addresses || addresses.has(op.origin_address)) &&
(!blockIds || blockIds.has(op.block)) &&
(!input.constant ||
op.constants.some((literal) => literal.value === input.constant)) &&
(!input.kind || op.kind === input.kind),
),
input,
);
}
function sliceValues(model, values, input) {
const result = backwardSlice(model, input.seeds, {
maxOperations: input.max_operations,
stopAt: input.stop_at,
stopKinds: input.stop_kinds,
});
const frontier = new Map();
for (const op of result.operations)
for (const input of op.inputs) {
const value = values.get(input.value_id);
if (!value)
frontier.set(`${op.id}:${input.input_index}`, {
operation: op.id,
input_index: input.input_index,
reason: "native-null-input",
});
else if (!value.definition && !value.is_constant && !value.is_annotation)
frontier.set(value.id, {
value_id: value.id,
reason: value.is_input ? "function-input" : "definition-unavailable",
});
}
return {
...result,
value_frontier: [...frontier.values()],
coverage: {
...result.coverage,
status:
result.coverage.status === "complete-for-printed-definitions"
? "complete-for-retained-value-definitions"
: result.coverage.status,
relation_authority: "native SSA value identities and operand indexes",
},
};
}
const projectedFailure = (error) => ({
code: error.code ?? "invalid_provider_output",
message: error.message,
details: error.details ?? {},
});
const captureFacets = (graph, calls, graphError, callError) =>
freeze({
code: { availability: "available" },
recovered_ir: graph
? { availability: "available" }
: { availability: "unavailable", error: graphError },
calls: calls
? { availability: "available", count: calls.count }
: { availability: "unavailable", error: callError },
values: graph
? { availability: "available", scope: "complete-for-recovered-graph" }
: calls
? {
availability: "available",
scope: "call-boundary-only",
count: calls.value_count,
}
: { availability: "unavailable", error: callError },
});
export function parseNativeIR(text, entry, expectedInput) {
const capture = parseNativeFunction(text, entry, expectedInput);
let graph, graphIndex, graphError, calls, callError;
if (capture.facts.ok) {
try {
freeze(capture.facts.value);
graphIndex = indexNative(capture.facts.value);
graph = createViews(capture.facts.value, capture.code, graphIndex);
} catch (error) {
graphError = projectedFailure(error);
}
} else graphError = projectedFailure(capture.facts.error);
if (capture.calls.ok) {
try {
const callIndex = indexCallFacet(capture.calls.value);
if (graph && graphIndex) validateCallGraph(callIndex, graphIndex);
calls = createCallViews(capture.calls.value, callIndex);
} catch (error) {
callError = projectedFailure(error);
}
} else callError = projectedFailure(capture.calls.error);
const unavailable = (facet, error) => () => {
throw new FactFailure(error.code, error.message, {
...error.details,
facet,
});
};
const unavailableGraph = unavailable("recovered_ir", graphError ?? {});
const unavailableCalls = unavailable("calls", callError ?? {});
const fallback = {
code: capture.code,
coverage: freeze({
scope: "native reconstructed code; recovered IR facet unavailable",
operations: null,
blocks: null,
control_flow: "unknown",
machine_semantic_equivalence: "unknown",
issues: [graphError?.message],
}),
...Object.fromEntries(
[
"operation",
"block",
"findConstant",
"uses",
"incoming",
"select",
"blocks",
"edges",
"slice",
].map((name) => [name, unavailableGraph]),
),
};
return Object.freeze({
...(graph ?? fallback),
language: capture.language,
calls: calls?.calls ?? unavailableCalls,
values: graph?.values ?? calls?.values ?? unavailableCalls,
facets: captureFacets(graph, calls, graphError, callError),
});
}