mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 07:02:13 +00:00
Initial builtins
This commit is contained in:
parent
28600bbeb7
commit
c0300c1e18
68
assembly.d.ts
vendored
68
assembly.d.ts
vendored
@ -1,5 +1,3 @@
|
||||
// types
|
||||
|
||||
/** An 8-bit signed integer. */
|
||||
declare type i8 = number;
|
||||
/** A 16-bit signed integer. */
|
||||
@ -27,78 +25,16 @@ declare type f32 = number;
|
||||
/** A 64-bit float. */
|
||||
declare type f64 = number;
|
||||
|
||||
// builtins
|
||||
|
||||
/** Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer. */
|
||||
declare function rotl<T>(value: T, shift: T): T;
|
||||
/** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */
|
||||
declare function rotr<T>(value: T, shift: T): T;
|
||||
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
|
||||
declare function clz<T>(value: T): T;
|
||||
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
|
||||
declare function ctz<T>(value: T): T;
|
||||
/** Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer. */
|
||||
declare function popcnt<T>(value: T): T;
|
||||
|
||||
/** Computes the absolute value of a 32-bit or 64-bit float. */
|
||||
declare function abs<T>(value: T): T;
|
||||
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
|
||||
declare function ceil<T>(value: T): T;
|
||||
/** Performs the floor operation on a 32-bit or 64-bit float. */
|
||||
declare function floor<T>(value: T): T;
|
||||
/** Calculates the square root of a 32-bit or 64-bit float. */
|
||||
declare function sqrt<T>(value: T): T;
|
||||
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
|
||||
declare function trunc<T>(value: T): T;
|
||||
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
|
||||
declare function nearest<T>(value: T): T;
|
||||
/** Determines the minimum of two 32-bit or 64-bit floats. If either operand is `NaN`, returns `NaN`. */
|
||||
declare function min<T>(left: T, right: T): T;
|
||||
/** Determines the maximum of two 32-bit or 64-bit floats. If either operand is `NaN`, returns `NaN`. */
|
||||
declare function max<T>(left: T, right: T): T;
|
||||
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
|
||||
declare function copysign<T>(x: T, y: T): T;
|
||||
|
||||
/** Reinterprets the bits of a value of type `T1` as type `T2`. Valid reinterpretations are i32 to/from f32 and i64 to/from f64. */
|
||||
declare function reinterpret<T1,T2>(value: T1): T2;
|
||||
/** Returns the current memory size in units of pages. One page is 64kb. */
|
||||
declare function current_memory(): i32;
|
||||
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
|
||||
declare function grow_memory(value: i32): i32;
|
||||
/** Emits an unreachable operation that results in a runtime error when executed. */
|
||||
declare function unreachable(): void;
|
||||
|
||||
/** Loads a value of the specified type from memory. */
|
||||
declare function load<T>(offset: usize): T;
|
||||
/** Stores a value of the specified type to memory. */
|
||||
declare function store<T>(offset: usize, value: T): void;
|
||||
/** Determines the byte size of the specified core or class type. Compiles to a constant. */
|
||||
declare function sizeof<T>(): usize;
|
||||
/** Gets the underlying pointer value of a class type. */
|
||||
declare function pointerof<T>(cls: T): usize;
|
||||
/** Creates a class type from its underlying pointer value. */
|
||||
declare function classof<T>(ptr: usize): T;
|
||||
|
||||
// standard library
|
||||
|
||||
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
|
||||
declare const NaN: number;
|
||||
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
|
||||
declare const Infinity: number;
|
||||
|
||||
/** Tests if a 32-bit or 64-bit float is NaN. */
|
||||
declare function isNaN<T>(value: T): bool;
|
||||
/** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */
|
||||
declare function isFinite<T>(value: T): bool;
|
||||
|
||||
/** A decorator marking a function or class as global. */
|
||||
declare function global(name?: string): any;
|
||||
/** A decorator marking a function as ideally being inlined. */
|
||||
declare function inline(): any;
|
||||
/** A decorator marking a class that manually manages its memory. */
|
||||
declare function allocates(): any;
|
||||
|
||||
declare function operator(token: string, fn: any): any;
|
||||
|
||||
/// <reference path="./std/builtins.d.ts" />
|
||||
/// <reference path="./std/array.d.ts" />
|
||||
/// <reference path="./std/map.d.ts" />
|
||||
/// <reference path="./std/math.d.ts" />
|
||||
|
231
src/compiler.ts
231
src/compiler.ts
@ -1,6 +1,6 @@
|
||||
import { PATH_DELIMITER } from "./constants";
|
||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||
import { Module, MemorySegment, ExpressionRef, UnaryOp, BinaryOp, NativeType, FunctionTypeRef } from "./module";
|
||||
import { Module, MemorySegment, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef } from "./module";
|
||||
import { Program, ClassPrototype, Class, Element, ElementKind, Enum, FunctionPrototype, Function, Global, Local, Namespace, Parameter } from "./program";
|
||||
import { CharCode, I64, U64, normalizePath, sb } from "./util";
|
||||
import { Token, Range } from "./tokenizer";
|
||||
@ -285,7 +285,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileGlobal(element: Global): bool {
|
||||
if (element.compiled)
|
||||
if (element.isCompiled)
|
||||
return true;
|
||||
const declaration: VariableLikeDeclarationStatement | null = element.declaration;
|
||||
let type: Type | null = element.type;
|
||||
@ -335,7 +335,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer));
|
||||
} else
|
||||
this.module.addGlobal(internalName, NativeType.I32, element.isMutable, initializer);
|
||||
return element.compiled = true;
|
||||
return element.isCompiled = true;
|
||||
}
|
||||
|
||||
// enums
|
||||
@ -348,9 +348,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileEnum(element: Enum): void {
|
||||
if (element.compiled)
|
||||
if (element.isCompiled)
|
||||
return;
|
||||
element.compiled = true;
|
||||
element.isCompiled = true;
|
||||
let previousInternalName: string | null = null;
|
||||
for (let [key, val] of element.members) {
|
||||
if (val.hasConstantValue) {
|
||||
@ -401,7 +401,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileFunction(instance: Function): void {
|
||||
if (instance.compiled)
|
||||
if (instance.isCompiled)
|
||||
return;
|
||||
|
||||
const declaration: FunctionDeclaration | null = instance.template.declaration;
|
||||
@ -412,7 +412,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, declaration.identifier.range);
|
||||
return;
|
||||
}
|
||||
instance.compiled = true;
|
||||
instance.isCompiled = true;
|
||||
|
||||
// compile statements
|
||||
const previousFunction: Function = this.currentFunction;
|
||||
@ -1221,7 +1221,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
left = this.compileExpression(expression.left, contextualType, false);
|
||||
right = this.compileExpression(expression.right, this.currentType);
|
||||
if (this.currentType.isAnyFloat)
|
||||
throw new Error("not implemented"); // TODO: internal fmod
|
||||
throw new Error("not implemented"); // TODO: internal fmod, possibly simply imported from JS
|
||||
op = this.currentType.isLongInteger
|
||||
? BinaryOp.RemI64
|
||||
: BinaryOp.RemI32;
|
||||
@ -1337,21 +1337,24 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef {
|
||||
// TODO
|
||||
/* const callee: Expression = expression.expression;
|
||||
switch (callee.kind) {
|
||||
case NodeKind.THIS:
|
||||
// use current class
|
||||
case NodeKind.PROPERTYACCESS:
|
||||
// compile, use class in currentType?
|
||||
case NodeKind.IDENTIFIER:
|
||||
// lookup identifier?
|
||||
} */
|
||||
const element: Element | null = this.program.resolveElement(expression.expression, this.currentFunction); // reports
|
||||
if (!element)
|
||||
return this.module.createUnreachable();
|
||||
if (element.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
||||
const functionInstance: Function | null = (<FunctionPrototype>element).resolveInclTypeArguments(expression.typeArguments, this.currentFunction.contextualTypeArguments, expression); // reports
|
||||
const functionPrototype: FunctionPrototype = <FunctionPrototype>element;
|
||||
let functionInstance: Function | null = null;
|
||||
if (functionPrototype.isBuiltin) {
|
||||
sb.length = 0;
|
||||
for (let i: i32 = 0, k: i32 = expression.typeArguments.length; i < k; ++i) {
|
||||
let type: Type | null = this.program.resolveType(expression.typeArguments[i], this.currentFunction.contextualTypeArguments, true); // reports
|
||||
if (!type)
|
||||
return this.module.createUnreachable();
|
||||
sb.push(type.toString());
|
||||
}
|
||||
functionInstance = <Function | null>functionPrototype.instances.get(sb.join(","));
|
||||
} else {
|
||||
functionInstance = (<FunctionPrototype>element).resolveInclTypeArguments(expression.typeArguments, this.currentFunction.contextualTypeArguments, expression); // reports
|
||||
}
|
||||
if (!functionInstance)
|
||||
return this.module.createUnreachable();
|
||||
return this.compileCall(functionInstance, expression.arguments, expression);
|
||||
@ -1362,9 +1365,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
/** Compiles a call to a function. If an instance method, `this` is the first element in `argumentExpressions`. */
|
||||
compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): ExpressionRef {
|
||||
// when called, the function is considered reachable -> compile
|
||||
if (!functionInstance.compiled)
|
||||
this.compileFunction(functionInstance);
|
||||
|
||||
// validate and compile arguments
|
||||
const parameters: Parameter[] = functionInstance.parameters;
|
||||
@ -1385,7 +1385,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
const initializer: Expression | null = parameters[i].initializer;
|
||||
if (initializer) { // omitted, uses initializer
|
||||
// FIXME: here, the initializer is compiled in the caller's scope.
|
||||
// a solution could be to use a stub for each possible overload, calling the
|
||||
// a solution could be to use a stub for each possible overload, calling the
|
||||
// full function with optional arguments being part of the stub's body.
|
||||
operands[i] = this.compileExpression(initializer, parameters[i].type);
|
||||
} else { // too few arguments
|
||||
@ -1398,8 +1398,187 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// finally compile the call
|
||||
this.currentType = functionInstance.returnType;
|
||||
|
||||
let tempLocal: Local;
|
||||
if (functionInstance.isBuiltin) {
|
||||
switch (functionInstance.template.internalName) {
|
||||
|
||||
case "clz": // i32/i64.clz
|
||||
if (this.currentType.isLongInteger)
|
||||
return this.module.createUnary(UnaryOp.ClzI64, operands[0]);
|
||||
else if (this.currentType.isAnyInteger)
|
||||
return this.module.createUnary(UnaryOp.ClzI32, operands[0]);
|
||||
break;
|
||||
|
||||
case "ctz": // i32/i64.ctz
|
||||
if (this.currentType.isLongInteger)
|
||||
return this.module.createUnary(UnaryOp.CtzI64, operands[0]);
|
||||
else if (this.currentType.isAnyInteger)
|
||||
return this.module.createUnary(UnaryOp.CtzI32, operands[0]);
|
||||
break;
|
||||
|
||||
case "popcnt": // i32/i64.popcnt
|
||||
if (this.currentType.isLongInteger)
|
||||
return this.module.createUnary(UnaryOp.PopcntI64, operands[0]);
|
||||
else if (this.currentType.isAnyInteger)
|
||||
return this.module.createUnary(UnaryOp.PopcntI32, operands[0]);
|
||||
break;
|
||||
|
||||
case "rotl": // i32/i64.rotl
|
||||
if (this.currentType.isLongInteger)
|
||||
return this.module.createBinary(BinaryOp.RotlI64, operands[0], operands[1]);
|
||||
else if (this.currentType.isAnyInteger)
|
||||
return this.module.createBinary(BinaryOp.RotlI32, operands[0], operands[1]);
|
||||
break;
|
||||
|
||||
case "rotr": // i32/i64.rotr
|
||||
if (this.currentType.isLongInteger)
|
||||
return this.module.createBinary(BinaryOp.RotrI64, operands[0], operands[1]);
|
||||
else if (this.currentType.isAnyInteger)
|
||||
return this.module.createBinary(BinaryOp.RotrI32, operands[0], operands[1]);
|
||||
break;
|
||||
|
||||
case "abs": // f32/f64.abs
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createUnary(UnaryOp.AbsF64, operands[0]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createUnary(UnaryOp.AbsF32, operands[0]);
|
||||
break;
|
||||
|
||||
case "ceil": // f32/f64.ceil
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createUnary(UnaryOp.CeilF64, operands[0]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createUnary(UnaryOp.CeilF32, operands[0]);
|
||||
break;
|
||||
|
||||
case "copysign": // f32/f64.copysign
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createBinary(BinaryOp.CopysignF64, operands[0], operands[1]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createBinary(BinaryOp.CopysignF32, operands[0], operands[1]);
|
||||
break;
|
||||
|
||||
case "floor": // f32/f64.floor
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createUnary(UnaryOp.FloorF64, operands[0]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createUnary(UnaryOp.FloorF32, operands[0]);
|
||||
break;
|
||||
|
||||
case "max": // f32/f64.max
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createBinary(BinaryOp.MaxF64, operands[0], operands[1]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createBinary(BinaryOp.MaxF32, operands[0], operands[1]);
|
||||
break;
|
||||
|
||||
case "min": // f32/f64.min
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createBinary(BinaryOp.MinF64, operands[0], operands[1]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createBinary(BinaryOp.MinF32, operands[0], operands[1]);
|
||||
break;
|
||||
|
||||
case "nearest": // f32/f64.nearest
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createUnary(UnaryOp.NearestF64, operands[0]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createUnary(UnaryOp.NearestF32, operands[0]);
|
||||
break;
|
||||
|
||||
case "sqrt": // f32/f64.sqrt
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createUnary(UnaryOp.SqrtF64, operands[0]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createUnary(UnaryOp.SqrtF32, operands[0]);
|
||||
break;
|
||||
|
||||
case "trunc": // f32/f64.trunc
|
||||
if (this.currentType == Type.f64)
|
||||
return this.module.createUnary(UnaryOp.TruncF64, operands[0]);
|
||||
else if (this.currentType == Type.f32)
|
||||
return this.module.createUnary(UnaryOp.TruncF32, operands[0]);
|
||||
break;
|
||||
|
||||
case "current_memory":
|
||||
return this.module.createHost(HostOp.CurrentMemory);
|
||||
|
||||
case "grow_memory":
|
||||
this.warning(DiagnosticCode.Operation_is_unsafe, reportNode.range); // unsure
|
||||
return this.module.createHost(HostOp.GrowMemory, null, operands);
|
||||
|
||||
case "unreachable":
|
||||
return this.module.createUnreachable();
|
||||
|
||||
case "sizeof": // T.size
|
||||
if (this.options.target == Target.WASM64)
|
||||
return this.module.createI64(Math.ceil(functionInstance.typeArguments[0].size / 8), 0);
|
||||
return this.module.createI32(Math.ceil(functionInstance.typeArguments[0].size / 8));
|
||||
|
||||
case "isNaN": // value != value
|
||||
if (functionInstance.typeArguments[0] == Type.f64) {
|
||||
tempLocal = this.currentFunction.addLocal(Type.f64);
|
||||
return this.module.createBinary(BinaryOp.NeF64,
|
||||
this.module.createTeeLocal(tempLocal.index, operands[0]),
|
||||
this.module.createGetLocal(tempLocal.index, NativeType.F64)
|
||||
);
|
||||
} else if (functionInstance.typeArguments[0] == Type.f32) {
|
||||
tempLocal = this.currentFunction.addLocal(Type.f32);
|
||||
return this.module.createBinary(BinaryOp.NeF32,
|
||||
this.module.createTeeLocal(tempLocal.index, operands[0]),
|
||||
this.module.createGetLocal(tempLocal.index, NativeType.F64)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "isFinite": // value != value ? false : abs(value) != Infinity
|
||||
if (functionInstance.typeArguments[0] == Type.f64) {
|
||||
tempLocal = this.currentFunction.addLocal(Type.f64);
|
||||
return this.module.createSelect(
|
||||
this.module.createBinary(BinaryOp.NeF64,
|
||||
this.module.createTeeLocal(tempLocal.index, operands[0]),
|
||||
this.module.createGetLocal(tempLocal.index, NativeType.F64)
|
||||
),
|
||||
this.module.createI32(0),
|
||||
this.module.createBinary(BinaryOp.NeF64,
|
||||
this.module.createUnary(UnaryOp.AbsF64,
|
||||
this.module.createGetLocal(tempLocal.index, NativeType.F64)
|
||||
),
|
||||
this.module.createF64(Infinity)
|
||||
)
|
||||
);
|
||||
} else if (functionInstance.typeArguments[0] == Type.f32) {
|
||||
tempLocal = this.currentFunction.addLocal(Type.f32);
|
||||
return this.module.createSelect(
|
||||
this.module.createBinary(BinaryOp.NeF32,
|
||||
this.module.createTeeLocal(tempLocal.index, operands[0]),
|
||||
this.module.createGetLocal(tempLocal.index, NativeType.F32)
|
||||
),
|
||||
this.module.createI32(0),
|
||||
this.module.createBinary(BinaryOp.NeF32,
|
||||
this.module.createUnary(UnaryOp.AbsF32,
|
||||
this.module.createGetLocal(tempLocal.index, NativeType.F32)
|
||||
),
|
||||
this.module.createF32(Infinity)
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.error(DiagnosticCode.Operation_not_supported, reportNode.range);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
if (!functionInstance.isCompiled)
|
||||
this.compileFunction(functionInstance);
|
||||
|
||||
// imported function
|
||||
if (functionInstance.isImport)
|
||||
return this.module.createCallImport(functionInstance.internalName, operands, typeToNativeType(functionInstance.returnType));
|
||||
|
||||
// internal function
|
||||
return this.module.createCall(functionInstance.internalName, operands, typeToNativeType(functionInstance.returnType));
|
||||
}
|
||||
|
||||
@ -1498,8 +1677,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case LiteralKind.FLOAT: {
|
||||
const floatValue: f64 = (<FloatLiteralExpression>expression).value;
|
||||
if (contextualType == Type.f32 && (Math.fround(floatValue) as f64) == floatValue)
|
||||
return this.module.createF32(floatValue);
|
||||
if (contextualType == Type.f32)
|
||||
return this.module.createF32(Math.fround(floatValue));
|
||||
this.currentType = Type.f64;
|
||||
return this.module.createF64(floatValue);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ export enum DiagnosticCode {
|
||||
Conversion_from_type_0_to_1_requires_an_explicit_cast = 100,
|
||||
Basic_type_0_cannot_be_nullable = 101,
|
||||
Operation_not_supported = 102,
|
||||
Operation_is_unsafe = 103,
|
||||
Unterminated_string_literal = 1002,
|
||||
Identifier_expected = 1003,
|
||||
_0_expected = 1005,
|
||||
@ -70,6 +71,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 100: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
|
||||
case 101: return "Basic type '{0}' cannot be nullable.";
|
||||
case 102: return "Operation not supported.";
|
||||
case 103: return "Operation is unsafe.";
|
||||
case 1002: return "Unterminated string literal.";
|
||||
case 1003: return "Identifier expected.";
|
||||
case 1005: return "'{0}' expected.";
|
||||
|
@ -2,6 +2,7 @@
|
||||
"Conversion from type '{0}' to '{1}' requires an explicit cast.": 100,
|
||||
"Basic type '{0}' cannot be nullable.": 101,
|
||||
"Operation not supported.": 102,
|
||||
"Operation is unsafe.": 103,
|
||||
|
||||
"Unterminated string literal.": 1002,
|
||||
"Identifier expected.": 1003,
|
||||
|
136
src/program.ts
136
src/program.ts
@ -81,22 +81,8 @@ export class Program extends DiagnosticEmitter {
|
||||
/** Initializes the program and its elements prior to compilation. */
|
||||
initialize(target: Target = Target.WASM32): void {
|
||||
this.target = target;
|
||||
this.types = new Map([ // replaces typesStub
|
||||
["i8", Type.i8],
|
||||
["i16", Type.i16],
|
||||
["i32", Type.i32],
|
||||
["i64", Type.i64],
|
||||
["isize", target == Target.WASM64 ? Type.isize64 : Type.isize32],
|
||||
["u8", Type.u8],
|
||||
["u16", Type.u16],
|
||||
["u32", Type.u32],
|
||||
["u64", Type.u64],
|
||||
["usize", target == Target.WASM64 ? Type.usize64 : Type.usize32],
|
||||
["bool", Type.bool],
|
||||
["f32", Type.f32],
|
||||
["f64", Type.f64],
|
||||
["void", Type.void]
|
||||
]);
|
||||
|
||||
initializeBuiltins(this);
|
||||
|
||||
const queuedExports: Map<string,QueuedExport> = new Map();
|
||||
const queuedImports: QueuedImport[] = new Array();
|
||||
@ -621,7 +607,9 @@ export abstract class Element {
|
||||
program: Program;
|
||||
internalName: string;
|
||||
globalExportName: string | null = null;
|
||||
compiled: bool = false;
|
||||
isCompiled: bool = false;
|
||||
isImport: bool = false;
|
||||
isBuiltin: bool = false;
|
||||
|
||||
constructor(program: Program, internalName: string) {
|
||||
this.program = program;
|
||||
@ -811,7 +799,7 @@ export class FunctionPrototype extends Element {
|
||||
let resolvedTypeArguments: Type[] | null;
|
||||
if (this.isGeneric) {
|
||||
if (!this.declaration)
|
||||
throw new Error("not implemented"); // generic builtin
|
||||
throw new Error("missing declaration");
|
||||
resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode);
|
||||
if (!resolvedTypeArguments)
|
||||
return null;
|
||||
@ -851,22 +839,23 @@ export class Function extends Element {
|
||||
private breakMinor: i32 = 0;
|
||||
|
||||
/** Constructs a new concrete function. */
|
||||
constructor(template: FunctionPrototype, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) {
|
||||
super(template.program, internalName);
|
||||
this.template = template;
|
||||
constructor(prototype: FunctionPrototype, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) {
|
||||
super(prototype.program, internalName);
|
||||
this.template = prototype;
|
||||
this.typeArguments = typeArguments;
|
||||
this.parameters = parameters;
|
||||
this.returnType = returnType;
|
||||
this.instanceMethodOf = instanceMethodOf;
|
||||
this.isBuiltin = prototype.isBuiltin;
|
||||
let localIndex: i32 = 0;
|
||||
if (instanceMethodOf) {
|
||||
this.locals.set("this", new Local(template.program, "this", localIndex++, instanceMethodOf.type));
|
||||
this.locals.set("this", new Local(prototype.program, "this", localIndex++, instanceMethodOf.type));
|
||||
for (let [name, type] of instanceMethodOf.contextualTypeArguments)
|
||||
this.contextualTypeArguments.set(name, type);
|
||||
}
|
||||
for (let i: i32 = 0, k: i32 = parameters.length; i < k; ++i) {
|
||||
const parameter: Parameter = parameters[i];
|
||||
this.locals.set(parameter.name, new Local(template.program, parameter.name, localIndex++, parameter.type));
|
||||
this.locals.set(parameter.name, new Local(prototype.program, parameter.name, localIndex++, parameter.type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1015,6 +1004,10 @@ export class Class extends Namespace {
|
||||
this.contextualTypeArguments.set(typeParameters[i].identifier.name, typeArguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
/** A yet unresvoled interface. */
|
||||
@ -1039,3 +1032,100 @@ export class Interface extends Class {
|
||||
super(template, internalName, typeArguments, base);
|
||||
}
|
||||
}
|
||||
|
||||
function initializeBuiltins(program: Program): void {
|
||||
|
||||
// types
|
||||
|
||||
program.types = new Map([
|
||||
["i8", Type.i8],
|
||||
["i16", Type.i16],
|
||||
["i32", Type.i32],
|
||||
["i64", Type.i64],
|
||||
["isize", program.target == Target.WASM64 ? Type.isize64 : Type.isize32],
|
||||
["u8", Type.u8],
|
||||
["u16", Type.u16],
|
||||
["u32", Type.u32],
|
||||
["u64", Type.u64],
|
||||
["usize", program.target == Target.WASM64 ? Type.usize64 : Type.usize32],
|
||||
["bool", Type.bool],
|
||||
["f32", Type.f32],
|
||||
["f64", Type.f64],
|
||||
["void", Type.void]
|
||||
]);
|
||||
|
||||
// functions
|
||||
|
||||
const genericInt: Type[] = [ Type.i32, Type.i64 ];
|
||||
const genericFloat: Type[] = [ Type.f32, Type.f64 ];
|
||||
|
||||
addGenericUnaryBuiltin(program, "clz", genericInt);
|
||||
addGenericUnaryBuiltin(program, "ctz", genericInt);
|
||||
addGenericUnaryBuiltin(program, "popcnt", genericInt);
|
||||
addGenericBinaryBuiltin(program, "rotl", genericInt);
|
||||
addGenericBinaryBuiltin(program, "rotr", genericInt);
|
||||
|
||||
addGenericUnaryBuiltin(program, "abs", genericFloat);
|
||||
addGenericUnaryBuiltin(program, "ceil", genericFloat);
|
||||
addGenericUnaryBuiltin(program, "copysign", genericFloat);
|
||||
addGenericUnaryBuiltin(program, "floor", genericFloat);
|
||||
addGenericBinaryBuiltin(program, "max", genericFloat);
|
||||
addGenericBinaryBuiltin(program, "min", genericFloat);
|
||||
addGenericUnaryBuiltin(program, "nearest", genericFloat);
|
||||
addGenericUnaryBuiltin(program, "sqrt", genericFloat);
|
||||
addGenericUnaryBuiltin(program, "trunc", genericFloat);
|
||||
|
||||
addBuiltin(program, "current_memory", [], Type.i32);
|
||||
addBuiltin(program, "grow_memory", [ Type.i32 ], Type.i32);
|
||||
addBuiltin(program, "unreachable", [], Type.void);
|
||||
|
||||
addGenericUnaryTestBuiltin(program, "isNaN", genericFloat);
|
||||
addGenericUnaryTestBuiltin(program, "isFinite", genericFloat);
|
||||
|
||||
// TODO: load, store, sizeof
|
||||
// sizeof, for example, has varying Ts but really shouldn't provide an instance for each class
|
||||
}
|
||||
|
||||
function addBuiltin(program: Program, name: string, parameterTypes: Type[], returnType: Type) {
|
||||
let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null);
|
||||
prototype.isGeneric = false;
|
||||
prototype.isBuiltin = true;
|
||||
const k: i32 = parameterTypes.length;
|
||||
const parameters: Parameter[] = new Array(k);
|
||||
for (let i: i32 = 0; i < k; ++i)
|
||||
parameters[i] = new Parameter("arg" + i, parameterTypes[i], null);
|
||||
prototype.instances.set("", new Function(prototype, name, [], parameters, Type.bool, null));
|
||||
}
|
||||
|
||||
function addGenericUnaryBuiltin(program: Program, name: string, types: Type[]): void {
|
||||
let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null);
|
||||
prototype.isGeneric = true;
|
||||
prototype.isBuiltin = true;
|
||||
for (let i: i32 = 0, k = types.length; i < k; ++i) {
|
||||
const typeName: string = types[i].toString();
|
||||
prototype.instances.set(typeName, new Function(prototype, name + "<" + typeName + ">", [ types[i] ], [ new Parameter("value", types[i], null) ], types[i], null));
|
||||
}
|
||||
program.elements.set(name, prototype);
|
||||
}
|
||||
|
||||
function addGenericBinaryBuiltin(program: Program, name: string, types: Type[]): void {
|
||||
let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null);
|
||||
prototype.isGeneric = true;
|
||||
prototype.isBuiltin = true;
|
||||
for (let i: i32 = 0, k = types.length; i < k; ++i) {
|
||||
const typeName: string = types[i].toString();
|
||||
prototype.instances.set(typeName, new Function(prototype, name + "<" + typeName + ">", [ types[i], types[i] ], [ new Parameter("left", types[i], null), new Parameter("right", types[i], null) ], types[i], null));
|
||||
}
|
||||
program.elements.set(name, prototype);
|
||||
}
|
||||
|
||||
function addGenericUnaryTestBuiltin(program: Program, name: string, types: Type[]): void {
|
||||
let prototype: FunctionPrototype = new FunctionPrototype(program, name, null, null);
|
||||
prototype.isGeneric = true;
|
||||
prototype.isBuiltin = true;
|
||||
for (let i: i32 = 0, k = types.length; i < k; ++i) {
|
||||
const typeName: string = types[i].toString();
|
||||
prototype.instances.set(typeName, new Function(prototype, name + "<" + typeName + ">", [ types[i] ], [ new Parameter("value", types[i], null) ], Type.bool, null));
|
||||
}
|
||||
program.elements.set(name, prototype);
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ export class Type {
|
||||
case TypeKind.I8: return "i8";
|
||||
case TypeKind.I16: return "i16";
|
||||
case TypeKind.I32: return "i32";
|
||||
case TypeKind.I64: return "i64";
|
||||
case TypeKind.ISIZE: return "isize";
|
||||
case TypeKind.U8: return "u8";
|
||||
case TypeKind.U16: return "u16";
|
||||
|
59
std/builtins.d.ts
vendored
Normal file
59
std/builtins.d.ts
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
|
||||
declare function clz<T>(value: T): T;
|
||||
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
|
||||
declare function ctz<T>(value: T): T;
|
||||
/** Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer. */
|
||||
declare function popcnt<T>(value: T): T;
|
||||
/** Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer. */
|
||||
declare function rotl<T>(value: T, shift: T): T;
|
||||
/** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */
|
||||
declare function rotr<T>(value: T, shift: T): T;
|
||||
|
||||
/** Computes the absolute value of a 32-bit or 64-bit float. */
|
||||
declare function abs<T>(value: T): T;
|
||||
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
|
||||
declare function ceil<T>(value: T): T;
|
||||
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
|
||||
declare function copysign<T>(x: T, y: T): T;
|
||||
/** Performs the floor operation on a 32-bit or 64-bit float. */
|
||||
declare function floor<T>(value: T): T;
|
||||
/** Determines the maximum of two 32-bit or 64-bit floats. If either operand is `NaN`, returns `NaN`. */
|
||||
declare function max<T>(left: T, right: T): T;
|
||||
/** Determines the minimum of two 32-bit or 64-bit floats. If either operand is `NaN`, returns `NaN`. */
|
||||
declare function min<T>(left: T, right: T): T;
|
||||
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
|
||||
declare function nearest<T>(value: T): T;
|
||||
/** Reinterprets the bits of a value of type `T1` as type `T2`. Valid reinterpretations are i32 to/from f32 and i64 to/from f64. */
|
||||
declare function reinterpret<T1,T2>(value: T1): T2;
|
||||
/** Calculates the square root of a 32-bit or 64-bit float. */
|
||||
declare function sqrt<T>(value: T): T;
|
||||
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
|
||||
declare function trunc<T>(value: T): T;
|
||||
|
||||
/** Returns the current memory size in units of pages. One page is 64kb. */
|
||||
declare function current_memory(): i32;
|
||||
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
|
||||
declare function grow_memory(value: i32): i32;
|
||||
/** Emits an unreachable operation that results in a runtime error when executed. */
|
||||
declare function unreachable(): void;
|
||||
|
||||
/** Loads a value of the specified type from memory. */
|
||||
declare function load<T>(offset: usize): T;
|
||||
/** Stores a value of the specified type to memory. */
|
||||
declare function store<T>(offset: usize, value: T): void;
|
||||
/** Determines the byte size of the specified core or class type. Compiles to a constant. */
|
||||
declare function sizeof<T>(): usize;
|
||||
|
||||
// standard library
|
||||
|
||||
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
|
||||
declare const NaN: number;
|
||||
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
|
||||
declare const Infinity: number;
|
||||
|
||||
/** Tests if a 32-bit or 64-bit float is NaN. */
|
||||
declare function isNaN<T>(value: T): bool;
|
||||
/** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */
|
||||
declare function isFinite<T>(value: T): bool;
|
@ -31,9 +31,15 @@ const files: Map<string,string> = new Map([
|
||||
return -1;
|
||||
}
|
||||
import { sub } from "../other";
|
||||
export function what(): void {
|
||||
export function doCall(): void {
|
||||
sub(1,2);
|
||||
}
|
||||
export function doNaN(value: f32): bool {
|
||||
return isNaN<f32>(0.3);
|
||||
}
|
||||
export function doRotl(value: u16): u8 {
|
||||
return rotl<i32>(value, 2);
|
||||
}
|
||||
`],
|
||||
|
||||
["../other",
|
||||
@ -58,7 +64,7 @@ do {
|
||||
const program = parser.finish();
|
||||
const compiler = new Compiler(program);
|
||||
const module = compiler.compile();
|
||||
console.log(program.elements.keys());
|
||||
// console.log(program.elements.keys());
|
||||
|
||||
// module.optimize();
|
||||
module.validate();
|
||||
|
Loading…
x
Reference in New Issue
Block a user