Skip to content

Commit

Permalink
Check that builtins are implemented (#2636)
Browse files Browse the repository at this point in the history
  • Loading branch information
dcodeIO committed Jan 30, 2023
1 parent 5823c0e commit e22a599
Show file tree
Hide file tree
Showing 8 changed files with 1,379 additions and 1,193 deletions.
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

0 comments on commit e22a599

Please sign in to comment.