@asmlift/core 0.8.0 → 0.9.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.
Files changed (105) hide show
  1. package/README.md +34 -30
  2. package/package.json +1 -1
  3. package/src/aggregate.ts +248 -0
  4. package/src/backend/c.ts +5 -2
  5. package/src/backend/cfamily.ts +236 -37
  6. package/src/backend/cpp.ts +114 -9
  7. package/src/backend/pascal.ts +39 -0
  8. package/src/cdecl/constant.ts +59 -0
  9. package/src/cdecl/lex.ts +209 -0
  10. package/src/cdecl/parse.ts +975 -0
  11. package/src/cdecl/spell.ts +69 -0
  12. package/src/codegen-flags.ts +38 -6
  13. package/src/compiler-diagnostics.ts +194 -0
  14. package/src/contracts.ts +615 -79
  15. package/src/declare.ts +75 -5
  16. package/src/frontend/asmdata.ts +19 -5
  17. package/src/frontend/call-plan.ts +686 -0
  18. package/src/frontend/device-pins.ts +277 -0
  19. package/src/frontend/errors.ts +5 -0
  20. package/src/frontend/flags-edge.ts +101 -0
  21. package/src/frontend/fpu.ts +167 -0
  22. package/src/frontend/frame-objects.ts +2131 -0
  23. package/src/frontend/high-half.ts +2 -1
  24. package/src/frontend/local-object.ts +490 -0
  25. package/src/frontend/mips.ts +363 -40
  26. package/src/frontend/opaque.ts +97 -18
  27. package/src/frontend/ppc.ts +520 -129
  28. package/src/frontend/reloc-symbol.ts +94 -13
  29. package/src/frontend/splat.ts +108 -5
  30. package/src/frontend/ssa.ts +669 -76
  31. package/src/frontend/stackargs.ts +120 -39
  32. package/src/frontend/thumb.ts +2764 -2018
  33. package/src/ir/alias.ts +29 -0
  34. package/src/ir/core.ts +101 -3
  35. package/src/ir/discipline.ts +248 -0
  36. package/src/ir/float-bits.ts +47 -0
  37. package/src/ir/opcodes.ts +84 -112
  38. package/src/ir/parse.ts +1 -0
  39. package/src/ir/types.ts +118 -2
  40. package/src/ir/verify.ts +150 -3
  41. package/src/l3/address.ts +5 -0
  42. package/src/l3/argcopy.ts +38 -6
  43. package/src/l3/ast.ts +186 -14
  44. package/src/l3/coalesce.ts +21 -13
  45. package/src/l3/dce.ts +32 -10
  46. package/src/l3/gates.ts +9 -3
  47. package/src/l3/hoist.ts +8 -7
  48. package/src/l3/initfirst.ts +27 -9
  49. package/src/l3/intcell.ts +97 -0
  50. package/src/l3/mentions.ts +10 -4
  51. package/src/l3/narrowdecl.ts +218 -0
  52. package/src/l3/nearbase.ts +10 -5
  53. package/src/l3/parkfirst.ts +8 -5
  54. package/src/l3/pollguard.ts +3 -2
  55. package/src/l3/ptrcell.ts +357 -0
  56. package/src/l3/regspell.ts +17 -3
  57. package/src/l3/reindex.ts +10 -7
  58. package/src/l3/symbol-refs.ts +104 -23
  59. package/src/l3/tailmerge.ts +6 -19
  60. package/src/l3/typing.ts +137 -25
  61. package/src/l3/unreduce.ts +4 -4
  62. package/src/l3/volstore.ts +96 -7
  63. package/src/mangle.ts +11 -1
  64. package/src/pattern/engine.ts +19 -16
  65. package/src/pipeline.ts +57 -12
  66. package/src/proto-context.ts +788 -0
  67. package/src/proto.ts +689 -50
  68. package/src/raise/arrays.ts +4 -3
  69. package/src/raise/declared-volatile.ts +131 -0
  70. package/src/raise/divpow2.ts +2 -2
  71. package/src/raise/errors.ts +17 -0
  72. package/src/raise/floathelpers.ts +243 -0
  73. package/src/raise/globalshape.ts +11 -5
  74. package/src/raise/gvn.ts +26 -5
  75. package/src/raise/narrow.ts +29 -2
  76. package/src/raise/narrowlocal.ts +11 -6
  77. package/src/raise/offsetnames.ts +510 -0
  78. package/src/raise/pairparams.ts +81 -0
  79. package/src/raise/paramwidth.ts +7 -0
  80. package/src/raise/pre-recovery.ts +78 -4
  81. package/src/raise/recover.ts +14 -10
  82. package/src/raise/retsink.ts +1 -1
  83. package/src/raise/shortcircuit.ts +41 -19
  84. package/src/raise/softdiv.ts +21 -23
  85. package/src/raise/struct-arrays.ts +5 -4
  86. package/src/raise/structs.ts +311 -98
  87. package/src/raise/truncload.ts +366 -0
  88. package/src/raise/widehelpers.ts +106 -0
  89. package/src/rank-declare.ts +30 -5
  90. package/src/rank-variations.ts +36 -8
  91. package/src/rank.ts +276 -80
  92. package/src/runtime-helpers.ts +287 -0
  93. package/src/stillborn.ts +289 -0
  94. package/src/structure/analysis.ts +1285 -448
  95. package/src/structure/hazards.ts +633 -58
  96. package/src/structure/local-statics.ts +381 -0
  97. package/src/structure/namecoalesce.ts +1 -1
  98. package/src/structure/redundant-test.ts +99 -0
  99. package/src/structure/structure.ts +1772 -395
  100. package/src/structure/switch-recover.ts +159 -78
  101. package/src/symbols.ts +38 -3
  102. package/src/target.ts +662 -59
  103. package/src/trace.ts +44 -24
  104. package/src/variation-definitions.ts +119 -1
  105. package/src/variation-tokens.ts +20 -6
package/src/declare.ts CHANGED
@@ -19,8 +19,10 @@
19
19
  // ldrb+lsl+asr where u8 is ldrb alone, so a guessed signedness is a wrong-bytes decl;
20
20
  // • `volatile` is load-bearing (a non-volatile MMIO decl lets the compiler fold/reorder
21
21
  // accesses), `const` is the ROM-table spelling;
22
- // • code symbols get `void Name(void);` ONLY when value-referenced — call targets are never
23
- // in `symbolRefs` (core excludes them: prototyping a called symbol is C89 poison);
22
+ // • code symbols get `void Name(void);` when value-referenced, and a CALL TARGET gets its own
23
+ // prototype re-spelled from the project's `FnProto` — only where that states both a typed
24
+ // parameter list and a `returns`, because `void Name(void);` over a call that passes
25
+ // arguments is C89 poison (core decides it: `spellableProto`);
24
26
  // • nothing guesses, with TWO documented exceptions: a SHAPED symbol without the facts to
25
27
  // declare faithfully is SKIPPED — the candidate then fails to compile LOUDLY and is
26
28
  // dropped by rankBy. Exception one is the 4-byte signless-non-pointer cell (see
@@ -51,6 +53,7 @@
51
53
  import { type StructFieldDecl, renderStructDecl } from './backend/cfamily';
52
54
  import { T } from './ir/types';
53
55
  import type { SymbolRef } from './l3/symbol-refs';
56
+ import { type AggregateLayout, type AggregateMember, declaredWidth, spellableType } from './proto';
54
57
  import {
55
58
  ENUM_IS_SIGNED,
56
59
  type SymbolInfo,
@@ -134,6 +137,43 @@ function structDecl(tag: string, layout: SymbolStructField[] | undefined, size:
134
137
  return renderStructDecl(tag, fields);
135
138
  }
136
139
 
140
+ /** A struct the project's headers define, transcribed from their declaration so the compiler lays
141
+ * it out as it lays out theirs: each member in order with its extents and bit width, a nested
142
+ * struct or union inline, and every type spelled so that it needs nothing else declared — a
143
+ * pointer as `void *`, an enum as an inline enum of its own, a scalar the prelude does not name
144
+ * as the prelude's type of its width and signedness. Its members are what size it, not what is
145
+ * read of it: no emitted expression names one (aggregate.ts `aggregateType`). */
146
+ function declaredStructDecl(tag: string, layout: AggregateLayout): string {
147
+ let enums = 0;
148
+ const scalar = (spelling: string): string => {
149
+ const s = spelling
150
+ .replace(/\b(?:const|volatile)\b/g, ' ')
151
+ .trim()
152
+ .replace(/\s+/g, ' ');
153
+ if (s.endsWith('*')) {
154
+ return 'void *';
155
+ }
156
+ if (/^enum\b/.test(s)) {
157
+ return `enum { asmlift_${tag}_enum${enums++} }`;
158
+ }
159
+ if (s === 'float' || s === 'double' || spellableType(s)) {
160
+ return s;
161
+ }
162
+ const bits = declaredWidth(s);
163
+ return `${/^u\d|\bunsigned\b/.test(s) ? 'u' : 's'}${bits}`;
164
+ };
165
+ const body = (members: readonly AggregateMember[]): string =>
166
+ members
167
+ .map((m) => {
168
+ const type = typeof m.type === 'string' ? scalar(m.type) : `${m.type.kind} { ${body(m.type.members!)} }`;
169
+ const declarator = `${m.name}${(m.dims ?? []).map((n) => `[${n}]`).join('')}`;
170
+ const bits = m.bits !== undefined ? ` : ${m.bits}` : '';
171
+ return `${type}${type.endsWith('*') || declarator === '' ? '' : ' '}${declarator}${bits};`;
172
+ })
173
+ .join(' ');
174
+ return `struct ${tag} { ${body(layout.members!)} };`;
175
+ }
176
+
137
177
  /**
138
178
  * Render the declaration block for a candidate's recorded symbol references. Deterministic
139
179
  * (refs arrive name-sorted from core; struct decls dedupe by tag). The block is prepended by
@@ -144,7 +184,8 @@ function structDecl(tag: string, layout: SymbolStructField[] | undefined, size:
144
184
  export function renderDeclarations(refs: SymbolRef[]): string {
145
185
  const lines: string[] = [];
146
186
  const declaredTags = new Set<string>();
147
- for (const { name, info, access } of refs) {
187
+ const declaredTypedefs = new Set<string>();
188
+ for (const { name, info, access, proto, returned, holdsPointer } of refs) {
148
189
  // An address-cast macro declares itself: the header's own body, verbatim. It must NOT become
149
190
  // an `extern` — that is the whole point of the fact (an extern emits a relocated pool word
150
191
  // where the macro emits the numeric one the target shows).
@@ -152,10 +193,33 @@ export function renderDeclarations(refs: SymbolRef[]): string {
152
193
  lines.push(`#define ${name} ${info.macroBody}`);
153
194
  continue;
154
195
  }
196
+ // A CALL TARGET the project declared completely — its own prototype, re-spelled from the
197
+ // parameter and return type texts the project wrote. This is the one declaration here that
198
+ // is load-bearing for the CALL and not only for the name: an implicit declaration is `int`,
199
+ // which narrows a 64-bit return and adds the sign-extension the target does not have. A
200
+ // parameterless list is spelled `(void)`, never `()`, because `()` declares nothing about
201
+ // the arguments and gcc-2.9 then promotes them.
202
+ if (proto !== undefined) {
203
+ // A STRUCT IT RETURNS THROUGH MEMORY is the headers' type, spelled as they spell it, so it is
204
+ // defined here with the rest of what they own: by its tag, once, whoever else declares that
205
+ // tag — and a typedef name is bound to it on top.
206
+ if (returned?.declared !== undefined) {
207
+ if (!declaredTags.has(returned.name)) {
208
+ declaredTags.add(returned.name);
209
+ lines.push(declaredStructDecl(returned.name, returned.layout));
210
+ }
211
+ if (!/^(?:struct|union)\s/.test(returned.declared) && !declaredTypedefs.has(returned.declared)) {
212
+ declaredTypedefs.add(returned.declared);
213
+ lines.push(`typedef struct ${returned.name} ${returned.declared};`);
214
+ }
215
+ }
216
+ lines.push(`${proto.returns} ${name}(${proto.params.length > 0 ? proto.params.join(', ') : 'void'});`);
217
+ continue;
218
+ }
155
219
  if (info.kind === 'code') {
156
220
  // value-referenced code symbol ((u32)Func): any prototype makes the name visible, and
157
- // the address is arity-independent. Call targets never reach this module (core excludes
158
- // them from symbolRefs — see collectSymbolRefs).
221
+ // the address is arity-independent. A call target reaches this module only with a complete
222
+ // prototype, which the branch above already printed — see collectSymbolRefs.
159
223
  lines.push(`void ${name}(void);`);
160
224
  continue;
161
225
  }
@@ -247,6 +311,12 @@ export function renderDeclarations(refs: SymbolRef[]): string {
247
311
  // argument applies. For a `synthesized` one it does not — the width came from the
248
312
  // target's own asm, so this line is a hypothesis fitted to the bytes; see the module
249
313
  // note's "WHERE THE ONLY-LOSES-SCORE ARGUMENT STOPS".
314
+ // A global that holds a pointer (ref.holdsPointer) is `void *`, the type every object
315
+ // pointer converts to without a cast; the qualifiers bind to the cell.
316
+ if (holdsPointer) {
317
+ lines.push(`extern void *${info.volatile ? 'volatile ' : ''}${info.const ? 'const ' : ''}${name};`);
318
+ break;
319
+ }
250
320
  const t = access ? intType(access.width, access.signed) : null;
251
321
  lines.push(`extern ${quals(info)}${t ?? 'u32'} ${name};`);
252
322
  break;
@@ -29,10 +29,22 @@ export interface Reloc {
29
29
  export interface AsmData {
30
30
  sections: Map<string, Uint8Array>; // section name → raw bytes (file order)
31
31
  relocs: Reloc[];
32
- symbols: Map<string, { section: string; value: number }>; // symbol name → {section, offset-in-section}
32
+ symbols: Map<string, AsmSymbol>;
33
+ /** how many symbols the `objdump -t` listing printed — the symbol table's entries past its null one */
34
+ symbolCount: number;
33
35
  bigEndian: boolean;
34
36
  }
35
37
 
38
+ /** One `objdump -t` symbol: its section, offset in it and size, and `index`, its position in the
39
+ * listing counting from 1 — its symbol-table index, which is what a per-symbol side table in the
40
+ * object (mwcc's `.comment`, frontend/local-object.ts) is indexed by. */
41
+ export interface AsmSymbol {
42
+ section: string;
43
+ value: number;
44
+ size: number;
45
+ index: number;
46
+ }
47
+
36
48
  const readU32 = (b: Uint8Array, off: number, big: boolean): number =>
37
49
  big
38
50
  ? ((b[off] << 24) | (b[off + 1] << 16) | (b[off + 2] << 8) | b[off + 3]) >>> 0
@@ -112,7 +124,8 @@ export function parseAsmData(
112
124
  }
113
125
 
114
126
  // --- `objdump -t`: `VALUE FLAGS SECTION\tSIZE NAME` (section/size split by TAB) ---
115
- const symbols = new Map<string, { section: string; value: number }>();
127
+ const symbols = new Map<string, AsmSymbol>();
128
+ let symbolCount = 0;
116
129
  for (const line of symbolsDump.split('\n')) {
117
130
  const tab = line.indexOf('\t');
118
131
  if (tab < 0) {
@@ -121,14 +134,15 @@ export function parseAsmData(
121
134
  const left = line.slice(0, tab),
122
135
  right = line.slice(tab + 1);
123
136
  const lm = left.match(/^([0-9a-f]+)\s+.{6,8}\s(\S+)\s*$/i); // value … flags(7) section
124
- const rm = right.match(/^[0-9a-f]+\s+(.+?)\s*$/i); // size name
137
+ const rm = right.match(/^([0-9a-f]+)\s+(.+?)\s*$/i); // size name
125
138
  if (!lm || !rm) {
126
139
  continue;
127
140
  }
128
- symbols.set(rm[1], { section: lm[2], value: parseInt(lm[1], 16) });
141
+ symbolCount++;
142
+ symbols.set(rm[2], { section: lm[2], value: parseInt(lm[1], 16), size: parseInt(rm[1], 16), index: symbolCount });
129
143
  }
130
144
 
131
- return { sections, relocs, symbols, bigEndian };
145
+ return { sections, relocs, symbols, symbolCount, bigEndian };
132
146
  }
133
147
 
134
148
  /** Read a dense jump table's N target `.text` byte-offsets, or `null` if ANYTHING doesn't resolve