-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathfunction.ts
More file actions
111 lines (96 loc) · 2.82 KB
/
function.ts
File metadata and controls
111 lines (96 loc) · 2.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import type * as Ast from "#ast";
import * as Ir from "#ir";
import { buildBlock } from "./statements/index.js";
import { Process } from "./process.js";
/**
* Build a function
*/
export function* buildFunction(
name: string,
parameters: {
name: string;
type: Ir.Type;
}[],
body: Ast.Block,
): Process<Ir.Function> {
// Initialize function context
yield* Process.Functions.initialize(name, parameters);
// Build function body
yield* buildBlock(body);
// Ensure function has a terminator
{
const terminator = yield* Process.Blocks.currentTerminator();
if (!terminator) {
// Add implicit return
yield* Process.Blocks.terminate({
kind: "return",
value: undefined,
// No debug context - compiler-generated implicit return
operationDebug: {},
});
}
}
// Sync final block
yield* Process.Blocks.syncCurrent();
// Compute predecessors from the control flow graph
const blocksBeforeCompute = yield* Process.Functions.currentBlocks();
const blocks = computePredecessors(blocksBeforeCompute);
const params = yield* Process.Functions.currentParameters();
// Collect SSA variable metadata
const ssaVariables = yield* Process.Functions.collectSsaMetadata();
const module_ = yield* Process.Modules.current();
const function_: Ir.Function = {
name,
parameters: params,
entry: "entry",
blocks,
ssaVariables: ssaVariables.size > 0 ? ssaVariables : undefined,
loc: body.loc ?? undefined,
sourceId: module_.sourceId,
};
return function_;
}
/**
* Compute predecessors for all blocks based on their terminators
*/
function computePredecessors(
blocks: Map<string, Ir.Block>,
): Map<string, Ir.Block> {
// Create new blocks with fresh predecessor sets
const result = new Map<string, Ir.Block>();
// First pass: create all blocks with empty predecessors
for (const [id, block] of blocks) {
result.set(id, {
...block,
predecessors: new Set<string>(),
});
}
// Second pass: add predecessors based on terminators
for (const [sourceId, block] of blocks) {
const terminator = block.terminator;
if (!terminator) continue;
// Add edges based on terminator type
switch (terminator.kind) {
case "jump": {
const targetBlock = result.get(terminator.target);
if (targetBlock) {
targetBlock.predecessors.add(sourceId);
}
break;
}
case "branch": {
const trueBlock = result.get(terminator.trueTarget);
if (trueBlock) {
trueBlock.predecessors.add(sourceId);
}
const falseBlock = result.get(terminator.falseTarget);
if (falseBlock) {
falseBlock.predecessors.add(sourceId);
}
break;
}
// "return" and "unreachable" have no successors
}
}
return result;
}