Skip to content

Commit e22a599

Browse files
authored
Check that builtins are implemented (#2636)
1 parent 5823c0e commit e22a599

File tree

8 files changed

+1379
-1193
lines changed

8 files changed

+1379
-1193
lines changed

Diff for: src/builtins.ts

+1,282-1,124
Large diffs are not rendered by default.

Diff for: src/compiler.ts

+42-40
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
import {
77
BuiltinNames,
8-
BuiltinContext,
9-
builtins,
10-
function_builtins,
8+
BuiltinFunctionContext,
9+
BuiltinVariableContext,
10+
builtinFunctions,
11+
builtinVariables_onAccess,
12+
builtinVariables_onCompile,
1113
compileVisitGlobals,
1214
compileVisitMembers,
1315
compileRTTI
@@ -509,17 +511,6 @@ export class Compiler extends DiagnosticEmitter {
509511
let startFunctionBody = this.currentBody;
510512
assert(startFunctionBody.length == 0);
511513

512-
// add mutable data, heap and rtti offset dummies
513-
if (options.isWasm64) {
514-
module.addGlobal(BuiltinNames.data_end, TypeRef.I64, true, module.i64(0));
515-
module.addGlobal(BuiltinNames.heap_base, TypeRef.I64, true, module.i64(0));
516-
module.addGlobal(BuiltinNames.rtti_base, TypeRef.I64, true, module.i64(0));
517-
} else {
518-
module.addGlobal(BuiltinNames.data_end, TypeRef.I32, true, module.i32(0));
519-
module.addGlobal(BuiltinNames.heap_base, TypeRef.I32, true, module.i32(0));
520-
module.addGlobal(BuiltinNames.rtti_base, TypeRef.I32, true, module.i32(0));
521-
}
522-
523514
// compile entry file(s) while traversing reachable elements
524515
let files = program.filesByName;
525516
// TODO: for (let file of files.values()) {
@@ -1174,13 +1165,13 @@ export class Compiler extends DiagnosticEmitter {
11741165
}
11751166
}
11761167

1177-
// Handle ambient builtins like '__heap_base' that need to be resolved but are added explicitly
1178-
if (global.is(CommonFlags.Ambient) && global.hasDecorator(DecoratorFlags.Builtin)) {
1168+
// Handle builtins like '__heap_base' that need to be resolved but are added explicitly
1169+
if (global.hasDecorator(DecoratorFlags.Builtin)) {
11791170
let internalName = global.internalName;
1180-
if (internalName == BuiltinNames.data_end) this.runtimeFeatures |= RuntimeFeatures.Data;
1181-
else if (internalName == BuiltinNames.stack_pointer) this.runtimeFeatures |= RuntimeFeatures.Stack;
1182-
else if (internalName == BuiltinNames.heap_base) this.runtimeFeatures |= RuntimeFeatures.Heap;
1183-
else if (internalName == BuiltinNames.rtti_base) this.runtimeFeatures |= RuntimeFeatures.Rtti;
1171+
if (builtinVariables_onCompile.has(internalName)) { // optional
1172+
let fn = assert(builtinVariables_onCompile.get(internalName));
1173+
fn(new BuiltinVariableContext(this, global));
1174+
}
11841175
pendingElements.delete(global);
11851176
return true;
11861177
}
@@ -6114,7 +6105,7 @@ export class Compiler extends DiagnosticEmitter {
61146105
);
61156106
}
61166107
let callee = expression.expression;
6117-
let ctx = new BuiltinContext(
6108+
let ctx = new BuiltinFunctionContext(
61186109
this,
61196110
prototype,
61206111
typeArguments,
@@ -6126,26 +6117,17 @@ export class Compiler extends DiagnosticEmitter {
61266117
expression,
61276118
false
61286119
);
6129-
// global builtins
6130-
let internalName = prototype.internalName;
6131-
if (builtins.has(internalName)) {
6132-
let fn = assert(builtins.get(internalName));
6133-
return fn(ctx);
6134-
}
6135-
// class builtins
6136-
let parent = prototype.parent;
6137-
if (parent.kind == ElementKind.Class) {
6138-
let classPrototype = (<Class>parent).prototype;
6139-
if (classPrototype == this.program.functionPrototype) {
6140-
let methodName = prototype.name;
6141-
if (function_builtins.has(methodName)) {
6142-
let fn = assert(function_builtins.get(methodName));
6143-
return fn(ctx);
6144-
}
6145-
}
6120+
let internalName: string;
6121+
if (prototype.is(CommonFlags.Instance)) {
6122+
// omit generic name components, e.g. in `Function<...>#call`
6123+
let parent = assert(prototype.getBoundClassOrInterface());
6124+
internalName = `${parent.prototype.internalName}#${prototype.name}`;
6125+
} else {
6126+
internalName = prototype.internalName;
61466127
}
6147-
assert(false);
6148-
return this.module.unreachable();
6128+
assert(builtinFunctions.has(internalName)); // checked earlier
6129+
let fn = assert(builtinFunctions.get(internalName));
6130+
return fn(ctx);
61496131
}
61506132

61516133
/**
@@ -7361,6 +7343,9 @@ export class Compiler extends DiagnosticEmitter {
73617343
return module.unreachable();
73627344
}
73637345
assert(globalType != Type.void);
7346+
if (global.hasDecorator(DecoratorFlags.Builtin)) {
7347+
return this.compileIdentifierExpressionBuiltin(global, expression, contextualType);
7348+
}
73647349
if (global.is(CommonFlags.Inlined)) {
73657350
return this.compileInlineConstant(global, contextualType, constraints);
73667351
}
@@ -7435,6 +7420,23 @@ export class Compiler extends DiagnosticEmitter {
74357420
return module.unreachable();
74367421
}
74377422

7423+
private compileIdentifierExpressionBuiltin(
7424+
element: VariableLikeElement,
7425+
expression: IdentifierExpression,
7426+
contextualType: Type
7427+
): ExpressionRef {
7428+
if (element.hasDecorator(DecoratorFlags.Unsafe)) this.checkUnsafe(expression, element.identifierNode);
7429+
let internalName = element.internalName;
7430+
assert(builtinVariables_onAccess.has(internalName)); // checked earlier
7431+
let fn = assert(builtinVariables_onAccess.get(internalName));
7432+
return fn(new BuiltinVariableContext(
7433+
this,
7434+
element,
7435+
contextualType,
7436+
expression
7437+
));
7438+
}
7439+
74387440
private compileInstanceOfExpression(
74397441
expression: InstanceOfExpression,
74407442
contextualType: Type,

Diff for: src/module.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2092,9 +2092,11 @@ export class Module {
20922092

20932093
removeGlobal(
20942094
name: string
2095-
): void {
2095+
): bool {
20962096
let cStr = this.allocStringCached(name);
2097+
if (!binaryen._BinaryenGetGlobal(this.ref, cStr)) return false;
20972098
binaryen._BinaryenRemoveGlobal(this.ref, cStr);
2099+
return true;
20982100
}
20992101

21002102
// tags

Diff for: src/program.ts

+32-14
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ import {
147147
} from "./parser";
148148

149149
import {
150-
BuiltinNames
150+
BuiltinNames,
151+
builtinFunctions,
152+
builtinVariables_onAccess
151153
} from "./builtins";
152154

153155
// Memory manager constants
@@ -474,8 +476,6 @@ export class Program extends DiagnosticEmitter {
474476
managedClasses: Map<i32,Class> = new Map();
475477
/** A set of unique function signatures contained in the program, by id. */
476478
uniqueSignatures: Map<string, Signature> = new Map<string, Signature>();
477-
/** Module exports. */
478-
moduleExports: Map<string,Element> = new Map();
479479
/** Module imports. */
480480
moduleImports: Map<string,Map<string,Element>> = new Map();
481481

@@ -1915,16 +1915,7 @@ export class Program extends DiagnosticEmitter {
19151915
let kind = DecoratorKind.fromNode(decorator.name);
19161916
let flag = DecoratorFlags.fromKind(kind);
19171917
if (flag) {
1918-
if (flag == DecoratorFlags.Builtin) {
1919-
if (!(acceptedFlags & flag) && !decorator.range.source.isLibrary) {
1920-
this.error(
1921-
DiagnosticCode.Decorator_0_is_not_valid_here,
1922-
decorator.range, decorator.name.range.toString()
1923-
);
1924-
} else {
1925-
flags |= flag;
1926-
}
1927-
} else if (!(acceptedFlags & flag)) {
1918+
if (!(acceptedFlags & flag)) {
19281919
this.error(
19291920
DiagnosticCode.Decorator_0_is_not_valid_here,
19301921
decorator.range, decorator.name.range.toString()
@@ -2154,12 +2145,21 @@ export class Program extends DiagnosticEmitter {
21542145
if (parent.is(CommonFlags.Ambient)) {
21552146
acceptedFlags |= DecoratorFlags.External;
21562147
}
2148+
if (declaration.range.source.isLibrary) {
2149+
acceptedFlags |= DecoratorFlags.Builtin;
2150+
}
21572151
let element = new FunctionPrototype(
21582152
name,
21592153
parent,
21602154
declaration,
21612155
this.checkDecorators(declaration.decorators, acceptedFlags)
21622156
);
2157+
if (element.hasDecorator(DecoratorFlags.Builtin) && !builtinFunctions.has(element.internalName)) {
2158+
this.error(
2159+
DiagnosticCode.Not_implemented_0,
2160+
declaration.range, `Builtin '${element.internalName}'`
2161+
);
2162+
}
21632163
if (isStatic) { // global function
21642164
assert(declaration.name.kind != NodeKind.Constructor);
21652165
if (!parent.add(name, element)) return null;
@@ -2579,7 +2579,7 @@ export class Program extends DiagnosticEmitter {
25792579
parent: Element
25802580
): FunctionPrototype | null {
25812581
let name = declaration.name.text;
2582-
let validDecorators = DecoratorFlags.Unsafe | DecoratorFlags.Builtin;
2582+
let validDecorators = DecoratorFlags.Unsafe;
25832583
if (declaration.is(CommonFlags.Ambient)) {
25842584
validDecorators |= DecoratorFlags.External | DecoratorFlags.ExternalJs;
25852585
} else {
@@ -2593,12 +2593,21 @@ export class Program extends DiagnosticEmitter {
25932593
validDecorators |= DecoratorFlags.Global;
25942594
}
25952595
}
2596+
if (declaration.range.source.isLibrary) {
2597+
validDecorators |= DecoratorFlags.Builtin;
2598+
}
25962599
let element = new FunctionPrototype(
25972600
name,
25982601
parent,
25992602
declaration,
26002603
this.checkDecorators(declaration.decorators, validDecorators)
26012604
);
2605+
if (element.hasDecorator(DecoratorFlags.Builtin) && !builtinFunctions.has(element.internalName)) {
2606+
this.error(
2607+
DiagnosticCode.Not_implemented_0,
2608+
declaration.range, `Builtin '${element.internalName}'`
2609+
);
2610+
}
26022611
if (!parent.add(name, element)) return null;
26032612
return element;
26042613
}
@@ -2800,12 +2809,21 @@ export class Program extends DiagnosticEmitter {
28002809
if (declaration.is(CommonFlags.Const)) {
28012810
acceptedFlags |= DecoratorFlags.Inline;
28022811
}
2812+
if (declaration.range.source.isLibrary) {
2813+
acceptedFlags |= DecoratorFlags.Builtin;
2814+
}
28032815
let element = new Global(
28042816
name,
28052817
parent,
28062818
this.checkDecorators(declaration.decorators, acceptedFlags),
28072819
declaration
28082820
);
2821+
if (element.hasDecorator(DecoratorFlags.Builtin) && !builtinVariables_onAccess.has(element.internalName)) {
2822+
this.error(
2823+
DiagnosticCode.Not_implemented_0,
2824+
declaration.range, `Builtin '${element.internalName}'`
2825+
);
2826+
}
28092827
if (!parent.add(name, element)) continue; // reports
28102828
}
28112829
}

Diff for: std/assembly/builtins.ts

-8
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,6 @@ export namespace i32 {
452452
@unsafe @builtin
453453
export declare function store(ptr: usize, value: i32, immOffset?: usize): void;
454454

455-
// @ts-ignore: decorator
456-
@builtin
457-
export declare function wait(ptr: usize, expected: i32, timeout: i64): AtomicWaitResult;
458-
459455
export namespace rmw8 {
460456

461457
// @ts-ignore: decorator
@@ -709,10 +705,6 @@ export namespace i64 {
709705
@unsafe @builtin
710706
export declare function store(ptr: usize, value: i64, immOffset?: usize): void;
711707

712-
// @ts-ignore: decorator
713-
@builtin
714-
export declare function wait(ptr: usize, expected: i64, timeout: i64): AtomicWaitResult;
715-
716708
export namespace rmw8 {
717709

718710
// @ts-ignore: decorator

Diff for: std/assembly/index.d.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,6 @@ declare namespace i32 {
387387
export function store16(ptr: usize, value: i32, immOffset?: usize): void;
388388
/** Atomically stores a 32-bit integer value to memory. */
389389
export function store(ptr: usize, value: i32, immOffset?: usize): void;
390-
/** Performs a wait operation on a 32-bit integer value in memory suspending this agent if the condition is met. */
391-
export function wait(ptr: usize, expected: i32, timeout?: i64): AtomicWaitResult;
392390
/** Atomic 32-bit integer read-modify-write operations on 8-bit values. */
393391
export namespace rmw8 {
394392
/** Atomically adds an 8-bit unsigned integer value in memory. */
@@ -522,8 +520,6 @@ declare namespace i64 {
522520
export function store32(ptr: usize, value: i64, immOffset?: usize): void;
523521
/** Atomically stores a 64-bit integer value to memory. */
524522
export function store(ptr: usize, value: i64, immOffset?: usize): void;
525-
/** Performs a wait operation on a 64-bit integer value in memory suspending this agent if the condition is met. */
526-
export function wait(ptr: usize, expected: i64, timeout?: i64): AtomicWaitResult;
527523
/** Atomic 64-bit integer read-modify-write operations on 8-bit values. */
528524
export namespace rmw8 {
529525
/** Atomically adds an 8-bit unsigned integer value in memory. */
@@ -1530,6 +1526,13 @@ declare namespace memory {
15301526
export function data(size: i32, align?: i32): usize;
15311527
/** Gets a pointer to a pre-initialized static chunk of memory. Alignment defaults to the size of `T`. Arguments must be compile-time constants. */
15321528
export function data<T>(values: T[], align?: i32): usize;
1529+
1530+
export namespace atomic {
1531+
/** Performs a wait operation on a 32-bit integer value in memory suspending this agent if the condition is met. */
1532+
export function wait32(ptr: usize, expected: i32, timeout?: i64): AtomicWaitResult;
1533+
/** Performs a wait operation on a 64-bit integer value in memory suspending this agent if the condition is met. */
1534+
export function wait64(ptr: usize, expected: i64, timeout?: i64): AtomicWaitResult;
1535+
}
15331536
}
15341537

15351538
/** Heap memory interface. */

Diff for: std/assembly/memory.ts

+11
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ export namespace memory {
2828
memmove(dst, src, n); // fallback if "bulk-memory" isn't enabled
2929
}
3030

31+
export namespace atomic {
32+
33+
// @ts-ignore: decorator
34+
@unsafe @builtin
35+
export declare function wait32(ptr: usize, expected: i32, timeout: i64): AtomicWaitResult;
36+
37+
// @ts-ignore: decorator
38+
@unsafe @builtin
39+
export declare function wait64(ptr: usize, expected: i64, timeout: i64): AtomicWaitResult;
40+
}
41+
3142
/** Initializes a memory segment. */
3243
// @ts-ignore: decorator
3344
@unsafe

Diff for: std/assembly/number.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { strtol, strtod } from "./util/string";
33

44
// @ts-ignore: decorator
55
@builtin @inline
6-
export const NaN: f64 = 0 / 0;
6+
export const NaN: f64 = 0 / 0; // context-aware
77

88
// @ts-ignore: decorator
99
@builtin @inline
10-
export const Infinity: f64 = 1 / 0;
10+
export const Infinity: f64 = 1 / 0; // context-aware
1111

1212
// @ts-ignore: decorator
1313
@builtin

0 commit comments

Comments
 (0)