@asmlift/core 0.8.1 → 0.10.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 (130) hide show
  1. package/README.md +36 -31
  2. package/package.json +1 -1
  3. package/src/aggregate.ts +397 -0
  4. package/src/backend/c.ts +6 -2
  5. package/src/backend/cfamily.ts +370 -56
  6. package/src/backend/cpp.ts +116 -10
  7. package/src/backend/pascal.ts +69 -11
  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 +777 -84
  15. package/src/declare.ts +132 -13
  16. package/src/frontend/asmdata.ts +26 -6
  17. package/src/frontend/call-plan.ts +1590 -0
  18. package/src/frontend/dead-reads.ts +628 -0
  19. package/src/frontend/device-pins.ts +296 -0
  20. package/src/frontend/emit.ts +4 -3
  21. package/src/frontend/errors.ts +5 -0
  22. package/src/frontend/flags-edge.ts +101 -0
  23. package/src/frontend/fpu.ts +374 -0
  24. package/src/frontend/frame-objects.ts +3171 -0
  25. package/src/frontend/frontend.ts +64 -3
  26. package/src/frontend/high-half.ts +55 -5
  27. package/src/frontend/jump-table.ts +530 -0
  28. package/src/frontend/local-object.ts +616 -0
  29. package/src/frontend/mips.ts +2392 -208
  30. package/src/frontend/opaque.ts +97 -18
  31. package/src/frontend/own-struct-return.ts +500 -0
  32. package/src/frontend/ppc.ts +3137 -267
  33. package/src/frontend/register-struct-call.ts +484 -0
  34. package/src/frontend/reloc-symbol.ts +109 -13
  35. package/src/frontend/splat.ts +108 -5
  36. package/src/frontend/ssa.ts +2098 -89
  37. package/src/frontend/stackargs.ts +172 -42
  38. package/src/frontend/thumb.ts +4563 -2091
  39. package/src/ir/alias.ts +71 -0
  40. package/src/ir/bits.ts +681 -1
  41. package/src/ir/core.ts +199 -6
  42. package/src/ir/discipline.ts +313 -0
  43. package/src/ir/entry.ts +159 -0
  44. package/src/ir/float-bits.ts +47 -0
  45. package/src/{structure → ir}/loops.ts +6 -2
  46. package/src/ir/opcodes.ts +193 -121
  47. package/src/ir/parse.ts +1 -0
  48. package/src/ir/types.ts +202 -2
  49. package/src/ir/verify.ts +219 -3
  50. package/src/l3/address.ts +5 -0
  51. package/src/l3/argcopy.ts +38 -6
  52. package/src/l3/ast.ts +467 -23
  53. package/src/l3/basecse.ts +33 -5
  54. package/src/l3/coalesce.ts +21 -13
  55. package/src/l3/constinit.ts +382 -0
  56. package/src/l3/dce.ts +34 -12
  57. package/src/l3/gates.ts +9 -3
  58. package/src/l3/hoist.ts +8 -7
  59. package/src/l3/initfirst.ts +68 -19
  60. package/src/l3/intcell.ts +98 -0
  61. package/src/l3/mentions.ts +10 -4
  62. package/src/l3/narrowdecl.ts +259 -0
  63. package/src/l3/nearbase.ts +10 -5
  64. package/src/l3/parkfirst.ts +8 -5
  65. package/src/l3/pollguard.ts +11 -4
  66. package/src/l3/ptrcell.ts +354 -0
  67. package/src/l3/ptrfield.ts +123 -8
  68. package/src/l3/regspell.ts +26 -7
  69. package/src/l3/reindex.ts +293 -103
  70. package/src/l3/storage.ts +98 -1
  71. package/src/l3/symbol-refs.ts +182 -23
  72. package/src/l3/tailmerge.ts +9 -20
  73. package/src/l3/typing.ts +237 -32
  74. package/src/l3/unreduce.ts +10 -9
  75. package/src/l3/volatileptr.ts +17 -54
  76. package/src/l3/volstore.ts +96 -7
  77. package/src/mangle.ts +41 -1
  78. package/src/pattern/engine.ts +373 -82
  79. package/src/pipeline.ts +589 -27
  80. package/src/proto-context.ts +1648 -0
  81. package/src/proto.ts +1203 -49
  82. package/src/raise/aggmembers.ts +290 -0
  83. package/src/raise/arrays.ts +8 -3
  84. package/src/raise/declared-volatile.ts +307 -0
  85. package/src/raise/divpow2.ts +2 -2
  86. package/src/raise/errors.ts +17 -0
  87. package/src/raise/floathelpers.ts +464 -0
  88. package/src/raise/globalshape.ts +11 -5
  89. package/src/raise/gvn.ts +29 -8
  90. package/src/raise/latch.ts +162 -6
  91. package/src/raise/memberarrays.ts +10 -4
  92. package/src/raise/narrow.ts +29 -2
  93. package/src/raise/narrowlocal.ts +11 -6
  94. package/src/raise/offsetnames.ts +494 -0
  95. package/src/raise/pairparams.ts +114 -0
  96. package/src/raise/paramwidth.ts +203 -6
  97. package/src/raise/pre-recovery.ts +174 -17
  98. package/src/raise/pre-structure.ts +54 -0
  99. package/src/raise/recover.ts +26 -15
  100. package/src/raise/reroll.ts +694 -0
  101. package/src/raise/retsink.ts +1 -1
  102. package/src/raise/shortcircuit.ts +66 -30
  103. package/src/raise/softdiv.ts +21 -23
  104. package/src/raise/storesign.ts +153 -0
  105. package/src/raise/struct-arrays.ts +34 -14
  106. package/src/raise/structs.ts +674 -98
  107. package/src/raise/truncload.ts +367 -0
  108. package/src/raise/unthread.ts +400 -0
  109. package/src/raise/widehelpers.ts +106 -0
  110. package/src/rank-declare.ts +143 -12
  111. package/src/rank-variations.ts +117 -31
  112. package/src/rank.ts +563 -122
  113. package/src/runtime-helpers.ts +405 -0
  114. package/src/stillborn.ts +289 -0
  115. package/src/structure/analysis.ts +1449 -449
  116. package/src/structure/arith-ops.ts +23 -0
  117. package/src/structure/globalaccess.ts +111 -9
  118. package/src/structure/hazards.ts +641 -114
  119. package/src/structure/local-statics.ts +517 -0
  120. package/src/structure/namecoalesce.ts +2 -2
  121. package/src/structure/pointer-spelling.ts +1106 -0
  122. package/src/structure/redundant-test.ts +99 -0
  123. package/src/structure/structure.ts +3478 -975
  124. package/src/structure/switch-recover.ts +403 -178
  125. package/src/symbols.ts +44 -3
  126. package/src/target.ts +1387 -71
  127. package/src/trace.ts +66 -28
  128. package/src/variation-definitions.ts +243 -13
  129. package/src/variation-gates.ts +2 -0
  130. package/src/variation-tokens.ts +25 -7
@@ -92,9 +92,10 @@
92
92
  // both configurations and with any window. No row demands the read spelling, and a variation with no
93
93
  // inhabitant is what "earn the level" forbids.
94
94
  import { type IrType, T, scalarTypeForAccess } from '../ir/types';
95
- import { cellAddress, inRange } from './address';
96
- import { type Expr, type SFn, type Stmt, stmtChildren } from './ast';
95
+ import { cellAddress, inRange, qualifiedBase } from './address';
96
+ import { type Expr, type SFn, type Stmt, dotBase, stmtChildren } from './ast';
97
97
  import { type Gate, firstRejection } from './gates';
98
+ import { derefStrideOk } from './typing';
98
99
 
99
100
  /** One STORE lvalue as the gates read it. */
100
101
  interface AccessCtx {
@@ -141,9 +142,6 @@ export const VOL_STORE_GATES: readonly Gate<AccessCtx>[] = [
141
142
  },
142
143
  ];
143
144
 
144
- /** Does this base already assert volatility — a `volatile` cast at any depth of the cast chain? */
145
- const qualifiedBase = (e: Expr): boolean => e.k === 'cast' && (e.volatile === true || qualifiedBase(e.e));
146
-
147
145
  /** The pointee the deref cast carries: the access's own scalar type. */
148
146
  const pointee = (ix: Extract<Expr, { k: 'index' }>): IrType => scalarTypeForAccess(ix.width, ix.signed);
149
147
 
@@ -169,8 +167,17 @@ function qualify(lval: Expr, window: readonly [number, number] | undefined): Exp
169
167
  if (firstRejection(VOL_STORE_GATES, ctx) !== null) {
170
168
  return lval;
171
169
  }
172
- // An existing scalar pointer cast takes the qualifier in place; a bare const gets one minted,
173
- // exactly the node backend/cfamily.ts's own deref legalization would have synthesized.
170
+ return qualifiedAccess(lval);
171
+ }
172
+
173
+ /** An indexed access with `volatile` on the pointee of its deref cast. An existing scalar pointer
174
+ * cast takes the qualifier in place; a bare base gets one minted, exactly the node
175
+ * backend/cfamily.ts's own deref legalization would have synthesized. Also how
176
+ * {@link qualifiedMemoryAccess} spells an indexed access the lift marked `volatile`. */
177
+ export function qualifiedAccess(lval: Extract<Expr, { k: 'index' }>): Extract<Expr, { k: 'index' }> {
178
+ if (qualifiedBase(lval.base)) {
179
+ return lval;
180
+ }
174
181
  const base: Expr =
175
182
  lval.base.k === 'cast' && lval.base.to.kind === 'ptr'
176
183
  ? { ...lval.base, volatile: true }
@@ -178,6 +185,88 @@ function qualify(lval: Expr, window: readonly [number, number] | undefined): Exp
178
185
  return { ...lval, base };
179
186
  }
180
187
 
188
+ /** A memory access the lift marked `volatile` (frontend/device-pins.ts), spelled through a
189
+ * `volatile` pointee: an indexed access by {@link qualifiedAccess}; a recovered struct member
190
+ * through the struct pointer it is reached by — `((volatile struct S *)0x40000B0)[ch].field_0`,
191
+ * `((volatile struct S *)p)->field_0` — whether that pointer is already a cast or a variable
192
+ * `typeOf` types as one; and a union view through the member it views. A named global — `gSym`,
193
+ * `gSym.field` — is returned unchanged, since its declaration owns its qualifiers. Null when the
194
+ * pointer the access is reached by has no type to cast it to, for the caller to refuse rather
195
+ * than drop the qualifier. */
196
+ export function qualifiedMemoryAccess(e: Expr, typeOf: (x: Expr) => IrType | undefined): Expr | null {
197
+ if (e.k === 'index') {
198
+ return qualifiedAccess(e);
199
+ }
200
+ if (e.k !== 'field') {
201
+ return e;
202
+ }
203
+ const element = dotBase(e);
204
+ if (element !== undefined) {
205
+ const base = qualifiedPointer(element.base, typeOf);
206
+ return base === null ? null : { ...e, base: { ...element, base } };
207
+ }
208
+ if (e.dot === true) {
209
+ if (e.base.k !== 'field') {
210
+ return e;
211
+ }
212
+ const member = qualifiedMemoryAccess(e.base, typeOf);
213
+ return member === null ? null : { ...e, base: member };
214
+ }
215
+ const base = qualifiedPointer(e.base, typeOf);
216
+ return base === null ? null : { ...e, base };
217
+ }
218
+
219
+ /** Does this access reach the object it names through a pointer cast — one the tree spells,
220
+ * `((struct T *)&gSym)->f`, or one the C-family printer puts on a base that does not stride the
221
+ * access as spelled (backend/cfamily.ts `legalizedIndexBase`), `((u16 *)&gSym)[3]` — rather than
222
+ * through the object's own name, element or member? The pointee of such a cast carries none of the
223
+ * object's declared qualifiers unless it says so itself. `typeOf` is the rendered C type the
224
+ * printer judges a base by. */
225
+ export function castOffDeclaration(e: Expr, typeOf: (x: Expr) => IrType | undefined): boolean {
226
+ if (e.k === 'field') {
227
+ // A dot member over an element prints that element's base as spelled.
228
+ const element = dotBase(e);
229
+ if (element !== undefined) {
230
+ return addressCast(element.base, typeOf);
231
+ }
232
+ return e.dot === true ? castOffDeclaration(e.base, typeOf) : addressCast(e.base, typeOf);
233
+ }
234
+ if (e.k !== 'index') {
235
+ return false;
236
+ }
237
+ if (addressCast(e.base, typeOf)) {
238
+ return true;
239
+ }
240
+ if (e.lead !== undefined && e.lead.length > 0) {
241
+ return false; // a multidimensional global's subscript prints its base as spelled
242
+ }
243
+ return !derefStrideOk(
244
+ typeOf(e.base) ?? (e.baseElem !== undefined ? T.ptr(e.baseElem) : undefined),
245
+ e.width,
246
+ e.signed,
247
+ );
248
+ }
249
+
250
+ /** Is there a cast between this address and the object it is taken from: one on it, under its
251
+ * arithmetic, or on the access whose element or member it is? */
252
+ const addressCast = (p: Expr, typeOf: (x: Expr) => IrType | undefined): boolean =>
253
+ p.k === 'cast' ||
254
+ (p.k === 'bin' && (addressCast(p.l, typeOf) || addressCast(p.r, typeOf))) ||
255
+ ((p.k === 'index' || p.k === 'field') && castOffDeclaration(p, typeOf));
256
+
257
+ /** A pointer expression carrying `volatile` on its pointee: a pointer cast takes it in place, and
258
+ * any other pointer is cast to its own type with it. */
259
+ function qualifiedPointer(p: Expr, typeOf: (x: Expr) => IrType | undefined): Expr | null {
260
+ if (qualifiedBase(p)) {
261
+ return p;
262
+ }
263
+ if (p.k === 'cast' && p.to.kind === 'ptr') {
264
+ return { ...p, volatile: true };
265
+ }
266
+ const t = typeOf(p);
267
+ return t?.kind === 'ptr' ? { k: 'cast', to: t, volatile: true, e: p } : null;
268
+ }
269
+
181
270
  /** How many stores this tree would qualify — the enumeration gate, so a function with no device
182
271
  * store costs one walk and no candidate.
183
272
  *
package/src/mangle.ts CHANGED
@@ -129,6 +129,36 @@ export function demangle(sym: string): CppSig | null {
129
129
  return { name, cls, params };
130
130
  }
131
131
 
132
+ /** How a class opens where gcc 2 names one: a length (`3Foo`), a nesting count (`Q21A1B`, and
133
+ * `Q_12_` past nine levels) or a template (`t3Vec1Zi`). A member function's const and volatile
134
+ * marks come before it (`get__C3Fooi`, `get__V3Fooi`, `get__CQ21A1Bi`), and CodeWarrior's class
135
+ * opens the same way, its marks following the class (`getStartHour__11PlayerStateCFv`). */
136
+ const GCC2_CLASS = String.raw`(?:\d|Q(?:\d|_\d+_)|t\d)`;
137
+
138
+ /** How many argument words a C++ symbol may be handed ahead of its declared parameters: 1 where it
139
+ * may name a MEMBER function, whose `this` the declared list leaves out; 0 where it cannot; and
140
+ * `undefined` for a constructor or destructor, which may also take a flag no declaration states
141
+ * (an in-charge or delete flag, beside `this`). Either mangling scheme a target here compiles with
142
+ * puts the class right after the `__`, length-prefixed or `Q<n>`-nested: CodeWarrior's
143
+ * `pick__3FooFiPi` (`__ct__3FooFv`, `__dt__3FooFv`), and gcc 2's `pick__3FooiPi` (a const method
144
+ * `pick__C3FooiPi`, a volatile one `pick__V3FooiPi`, a constructor `__3Foo`, a destructor `_$_3Foo`
145
+ * or `_._3Foo`, every one of which this abstains on, nested `_._Q21A1B` included), where a
146
+ * TEMPLATE class is spelled `t<n><name><args>` in the class's place (`get__t3Vec1Zii`, a
147
+ * constructor `__t3Vec1Zii`, `operator=` `__as__t3Vec1ZiRCt3Vec1Zi`). A free function (`f__Fii`)
148
+ * and a plain C name answer 0. A STATIC member mangles as a method does and takes no
149
+ * `this`, so 1 is "may", and a reader that needs the exact count must not take it as one.
150
+ *
151
+ * THE PATTERNS ERR TOWARDS A MEMBER, because the errors cost differently: a method read as free
152
+ * loses its `this` word, and the own-declaration bound (`proto.ts` `ownArgumentWords`) then turns
153
+ * its last parameter into an uninitialised local, where a C name read as a method only leaves one
154
+ * more register a parameter. */
155
+ export function implicitArgumentWords(sym: string): 0 | 1 | undefined {
156
+ if (/^__(?:ct|dt)__/.test(sym) || new RegExp(`^__${GCC2_CLASS}`).test(sym) || /^_[$.]_/.test(sym)) {
157
+ return undefined;
158
+ }
159
+ return new RegExp(`__[CV]*${GCC2_CLASS}`).test(sym) ? 1 : 0;
160
+ }
161
+
132
162
  /** A class qualifier off the front of `s` — `12RefCountable`, or `Q23zen17particleGenerator` for a
133
163
  * nested one — as its scope names and the remaining string; null when `s` does not open with one. */
134
164
  function parseQualifier(s: string): { scopes: string[]; rest: string } | null {
@@ -178,7 +208,17 @@ export function demangledName(sym: string): string | null {
178
208
  return null;
179
209
  }
180
210
 
181
- /** Spell a CppType as C++ source (`Vec *`, `unsigned int`). */
211
+ /** Spell a CppType as C++ source where no declarator is involved — a return type, a cast
212
+ * (`Vec *`, `unsigned int`). */
182
213
  export function spellType(t: CppType): string {
183
214
  return t.base + (t.ptr ? ' ' + '*'.repeat(t.ptr) : '');
184
215
  }
216
+
217
+ /** Declare a name of a CppType, C declarator rules: a pointer binds its `*` to the declarator
218
+ * (`Vec *p`), everything else is the prefix `spellType name`. The C-family backend's `cDeclare`
219
+ * says the same of an `IrType`; C++ has its own type vocabulary (`CppType`, from the mangled
220
+ * name) and so needs its own declarator, or one function spells its pointers two ways — the type
221
+ * prefix in the parameter list and the declarator form in the body. */
222
+ export function declareCpp(t: CppType, name: string): string {
223
+ return t.ptr > 0 ? `${t.base} ${'*'.repeat(t.ptr)}${name}` : `${t.base} ${name}`;
224
+ }