Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check that builtins are implemented #2636

Merged
merged 4 commits into from Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2,406 changes: 1,282 additions & 1,124 deletions src/builtins.ts

Large diffs are not rendered by default.

82 changes: 42 additions & 40 deletions src/compiler.ts
Expand Up @@ -5,9 +5,11 @@

import {
BuiltinNames,
BuiltinContext,
builtins,
function_builtins,
BuiltinFunctionContext,
BuiltinVariableContext,
builtinFunctions,
builtinVariables_onAccess,
builtinVariables_onCompile,
compileVisitGlobals,
compileVisitMembers,
compileRTTI
Expand Down Expand Up @@ -509,17 +511,6 @@ export class Compiler extends DiagnosticEmitter {
let startFunctionBody = this.currentBody;
assert(startFunctionBody.length == 0);

// add mutable data, heap and rtti offset dummies
if (options.isWasm64) {
module.addGlobal(BuiltinNames.data_end, TypeRef.I64, true, module.i64(0));
module.addGlobal(BuiltinNames.heap_base, TypeRef.I64, true, module.i64(0));
module.addGlobal(BuiltinNames.rtti_base, TypeRef.I64, true, module.i64(0));
} else {
module.addGlobal(BuiltinNames.data_end, TypeRef.I32, true, module.i32(0));
module.addGlobal(BuiltinNames.heap_base, TypeRef.I32, true, module.i32(0));
module.addGlobal(BuiltinNames.rtti_base, TypeRef.I32, true, module.i32(0));
}

// compile entry file(s) while traversing reachable elements
let files = program.filesByName;
// TODO: for (let file of files.values()) {
Expand Down Expand Up @@ -1174,13 +1165,13 @@ export class Compiler extends DiagnosticEmitter {
}
}

// Handle ambient builtins like '__heap_base' that need to be resolved but are added explicitly
if (global.is(CommonFlags.Ambient) && global.hasDecorator(DecoratorFlags.Builtin)) {
// Handle builtins like '__heap_base' that need to be resolved but are added explicitly
if (global.hasDecorator(DecoratorFlags.Builtin)) {
let internalName = global.internalName;
if (internalName == BuiltinNames.data_end) this.runtimeFeatures |= RuntimeFeatures.Data;
else if (internalName == BuiltinNames.stack_pointer) this.runtimeFeatures |= RuntimeFeatures.Stack;
else if (internalName == BuiltinNames.heap_base) this.runtimeFeatures |= RuntimeFeatures.Heap;
else if (internalName == BuiltinNames.rtti_base) this.runtimeFeatures |= RuntimeFeatures.Rtti;
if (builtinVariables_onCompile.has(internalName)) { // optional
let fn = assert(builtinVariables_onCompile.get(internalName));
fn(new BuiltinVariableContext(this, global));
}
pendingElements.delete(global);
return true;
}
Expand Down Expand Up @@ -6114,7 +6105,7 @@ export class Compiler extends DiagnosticEmitter {
);
}
let callee = expression.expression;
let ctx = new BuiltinContext(
let ctx = new BuiltinFunctionContext(
this,
prototype,
typeArguments,
Expand All @@ -6126,26 +6117,17 @@ export class Compiler extends DiagnosticEmitter {
expression,
false
);
// global builtins
let internalName = prototype.internalName;
if (builtins.has(internalName)) {
let fn = assert(builtins.get(internalName));
return fn(ctx);
}
// class builtins
let parent = prototype.parent;
if (parent.kind == ElementKind.Class) {
let classPrototype = (<Class>parent).prototype;
if (classPrototype == this.program.functionPrototype) {
let methodName = prototype.name;
if (function_builtins.has(methodName)) {
let fn = assert(function_builtins.get(methodName));
return fn(ctx);
}
}
let internalName: string;
if (prototype.is(CommonFlags.Instance)) {
// omit generic name components, e.g. in `Function<...>#call`
let parent = assert(prototype.getBoundClassOrInterface());
internalName = `${parent.prototype.internalName}#${prototype.name}`;
} else {
internalName = prototype.internalName;
}
assert(false);
return this.module.unreachable();
assert(builtinFunctions.has(internalName)); // checked earlier
let fn = assert(builtinFunctions.get(internalName));
return fn(ctx);
}

/**
Expand Down Expand Up @@ -7361,6 +7343,9 @@ export class Compiler extends DiagnosticEmitter {
return module.unreachable();
}
assert(globalType != Type.void);
if (global.hasDecorator(DecoratorFlags.Builtin)) {
return this.compileIdentifierExpressionBuiltin(global, expression, contextualType);
}
if (global.is(CommonFlags.Inlined)) {
return this.compileInlineConstant(global, contextualType, constraints);
}
Expand Down Expand Up @@ -7435,6 +7420,23 @@ export class Compiler extends DiagnosticEmitter {
return module.unreachable();
}

private compileIdentifierExpressionBuiltin(
element: VariableLikeElement,
expression: IdentifierExpression,
contextualType: Type
): ExpressionRef {
if (element.hasDecorator(DecoratorFlags.Unsafe)) this.checkUnsafe(expression, element.identifierNode);
let internalName = element.internalName;
assert(builtinVariables_onAccess.has(internalName)); // checked earlier
let fn = assert(builtinVariables_onAccess.get(internalName));
return fn(new BuiltinVariableContext(
this,
element,
contextualType,
expression
));
}

private compileInstanceOfExpression(
expression: InstanceOfExpression,
contextualType: Type,
Expand Down
4 changes: 3 additions & 1 deletion src/module.ts
Expand Up @@ -2092,9 +2092,11 @@ export class Module {

removeGlobal(
name: string
): void {
): bool {
let cStr = this.allocStringCached(name);
if (!binaryen._BinaryenGetGlobal(this.ref, cStr)) return false;
binaryen._BinaryenRemoveGlobal(this.ref, cStr);
return true;
}

// tags
Expand Down
46 changes: 32 additions & 14 deletions src/program.ts
Expand Up @@ -147,7 +147,9 @@ import {
} from "./parser";

import {
BuiltinNames
BuiltinNames,
builtinFunctions,
builtinVariables_onAccess
} from "./builtins";

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

Expand Down Expand Up @@ -1915,16 +1915,7 @@ export class Program extends DiagnosticEmitter {
let kind = DecoratorKind.fromNode(decorator.name);
let flag = DecoratorFlags.fromKind(kind);
if (flag) {
if (flag == DecoratorFlags.Builtin) {
if (!(acceptedFlags & flag) && !decorator.range.source.isLibrary) {
this.error(
DiagnosticCode.Decorator_0_is_not_valid_here,
decorator.range, decorator.name.range.toString()
);
} else {
flags |= flag;
}
} else if (!(acceptedFlags & flag)) {
if (!(acceptedFlags & flag)) {
this.error(
DiagnosticCode.Decorator_0_is_not_valid_here,
decorator.range, decorator.name.range.toString()
Expand Down Expand Up @@ -2154,12 +2145,21 @@ export class Program extends DiagnosticEmitter {
if (parent.is(CommonFlags.Ambient)) {
acceptedFlags |= DecoratorFlags.External;
}
if (declaration.range.source.isLibrary) {
acceptedFlags |= DecoratorFlags.Builtin;
}
let element = new FunctionPrototype(
name,
parent,
declaration,
this.checkDecorators(declaration.decorators, acceptedFlags)
);
if (element.hasDecorator(DecoratorFlags.Builtin) && !builtinFunctions.has(element.internalName)) {
this.error(
DiagnosticCode.Not_implemented_0,
declaration.range, `Builtin '${element.internalName}'`
);
}
if (isStatic) { // global function
assert(declaration.name.kind != NodeKind.Constructor);
if (!parent.add(name, element)) return null;
Expand Down Expand Up @@ -2579,7 +2579,7 @@ export class Program extends DiagnosticEmitter {
parent: Element
): FunctionPrototype | null {
let name = declaration.name.text;
let validDecorators = DecoratorFlags.Unsafe | DecoratorFlags.Builtin;
let validDecorators = DecoratorFlags.Unsafe;
if (declaration.is(CommonFlags.Ambient)) {
validDecorators |= DecoratorFlags.External | DecoratorFlags.ExternalJs;
} else {
Expand All @@ -2593,12 +2593,21 @@ export class Program extends DiagnosticEmitter {
validDecorators |= DecoratorFlags.Global;
}
}
if (declaration.range.source.isLibrary) {
validDecorators |= DecoratorFlags.Builtin;
}
let element = new FunctionPrototype(
name,
parent,
declaration,
this.checkDecorators(declaration.decorators, validDecorators)
);
if (element.hasDecorator(DecoratorFlags.Builtin) && !builtinFunctions.has(element.internalName)) {
this.error(
DiagnosticCode.Not_implemented_0,
declaration.range, `Builtin '${element.internalName}'`
);
}
if (!parent.add(name, element)) return null;
return element;
}
Expand Down Expand Up @@ -2800,12 +2809,21 @@ export class Program extends DiagnosticEmitter {
if (declaration.is(CommonFlags.Const)) {
acceptedFlags |= DecoratorFlags.Inline;
}
if (declaration.range.source.isLibrary) {
acceptedFlags |= DecoratorFlags.Builtin;
}
let element = new Global(
name,
parent,
this.checkDecorators(declaration.decorators, acceptedFlags),
declaration
);
if (element.hasDecorator(DecoratorFlags.Builtin) && !builtinVariables_onAccess.has(element.internalName)) {
this.error(
DiagnosticCode.Not_implemented_0,
declaration.range, `Builtin '${element.internalName}'`
);
}
if (!parent.add(name, element)) continue; // reports
}
}
Expand Down
8 changes: 0 additions & 8 deletions std/assembly/builtins.ts
Expand Up @@ -452,10 +452,6 @@ export namespace i32 {
@unsafe @builtin
export declare function store(ptr: usize, value: i32, immOffset?: usize): void;

// @ts-ignore: decorator
@builtin
export declare function wait(ptr: usize, expected: i32, timeout: i64): AtomicWaitResult;

export namespace rmw8 {

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

// @ts-ignore: decorator
@builtin
export declare function wait(ptr: usize, expected: i64, timeout: i64): AtomicWaitResult;

export namespace rmw8 {

// @ts-ignore: decorator
Expand Down
11 changes: 7 additions & 4 deletions std/assembly/index.d.ts
Expand Up @@ -387,8 +387,6 @@ declare namespace i32 {
export function store16(ptr: usize, value: i32, immOffset?: usize): void;
/** Atomically stores a 32-bit integer value to memory. */
export function store(ptr: usize, value: i32, immOffset?: usize): void;
/** Performs a wait operation on a 32-bit integer value in memory suspending this agent if the condition is met. */
export function wait(ptr: usize, expected: i32, timeout?: i64): AtomicWaitResult;
/** Atomic 32-bit integer read-modify-write operations on 8-bit values. */
export namespace rmw8 {
/** Atomically adds an 8-bit unsigned integer value in memory. */
Expand Down Expand Up @@ -522,8 +520,6 @@ declare namespace i64 {
export function store32(ptr: usize, value: i64, immOffset?: usize): void;
/** Atomically stores a 64-bit integer value to memory. */
export function store(ptr: usize, value: i64, immOffset?: usize): void;
/** Performs a wait operation on a 64-bit integer value in memory suspending this agent if the condition is met. */
export function wait(ptr: usize, expected: i64, timeout?: i64): AtomicWaitResult;
/** Atomic 64-bit integer read-modify-write operations on 8-bit values. */
export namespace rmw8 {
/** Atomically adds an 8-bit unsigned integer value in memory. */
Expand Down Expand Up @@ -1530,6 +1526,13 @@ declare namespace memory {
export function data(size: i32, align?: i32): usize;
/** Gets a pointer to a pre-initialized static chunk of memory. Alignment defaults to the size of `T`. Arguments must be compile-time constants. */
export function data<T>(values: T[], align?: i32): usize;

export namespace atomic {
/** Performs a wait operation on a 32-bit integer value in memory suspending this agent if the condition is met. */
export function wait32(ptr: usize, expected: i32, timeout?: i64): AtomicWaitResult;
/** Performs a wait operation on a 64-bit integer value in memory suspending this agent if the condition is met. */
export function wait64(ptr: usize, expected: i64, timeout?: i64): AtomicWaitResult;
}
}

/** Heap memory interface. */
Expand Down
11 changes: 11 additions & 0 deletions std/assembly/memory.ts
Expand Up @@ -28,6 +28,17 @@ export namespace memory {
memmove(dst, src, n); // fallback if "bulk-memory" isn't enabled
}

export namespace atomic {

// @ts-ignore: decorator
@unsafe @builtin
export declare function wait32(ptr: usize, expected: i32, timeout: i64): AtomicWaitResult;

// @ts-ignore: decorator
@unsafe @builtin
export declare function wait64(ptr: usize, expected: i64, timeout: i64): AtomicWaitResult;
}

/** Initializes a memory segment. */
// @ts-ignore: decorator
@unsafe
Expand Down
4 changes: 2 additions & 2 deletions std/assembly/number.ts
Expand Up @@ -3,11 +3,11 @@ import { strtol, strtod } from "./util/string";

// @ts-ignore: decorator
@builtin @inline
export const NaN: f64 = 0 / 0;
export const NaN: f64 = 0 / 0; // context-aware

// @ts-ignore: decorator
@builtin @inline
export const Infinity: f64 = 1 / 0;
export const Infinity: f64 = 1 / 0; // context-aware

// @ts-ignore: decorator
@builtin
Expand Down