Initial new rt integration

This commit is contained in:
dcode
2019-05-12 13:50:28 +02:00
parent dd2bdd0383
commit ba1a0c2369
52 changed files with 1066 additions and 2985 deletions

View File

@ -461,13 +461,19 @@ export namespace BuiltinSymbols {
export const v8x16_shuffle = "~lib/builtins/v8x16.shuffle";
// internals
export const HEAP_BASE = "~lib/builtins/HEAP_BASE";
export const RTTI_BASE = "~lib/builtins/RTTI_BASE";
export const idof = "~lib/builtins/idof";
export const visit_globals = "~lib/builtins/visit_globals";
export const visit_members = "~lib/builtins/visit_members";
// std/diagnostics.ts
export const ERROR = "~lib/diagnostics/ERROR";
export const WARNING = "~lib/diagnostics/WARNING";
export const INFO = "~lib/diagnostics/INFO";
// std/memory.ts
export const HEAP_BASE = "~lib/memory/HEAP_BASE";
export const memory_size = "~lib/memory/memory.size";
export const memory_grow = "~lib/memory/memory.grow";
export const memory_copy = "~lib/memory/memory.copy";
@ -477,8 +483,6 @@ export namespace BuiltinSymbols {
export const memory_reset = "~lib/memory/memory.reset";
// std/runtime.ts
export const RTTI_BASE = "~lib/runtime/RTTI_BASE";
export const runtime_id = "~lib/runtime/__runtime_id";
export const runtime_instanceof = "~lib/runtime/runtime.instanceof";
export const runtime_flags = "~lib/runtime/runtime.flags";
export const runtime_allocate = "~lib/util/runtime/allocate";
@ -486,8 +490,6 @@ export namespace BuiltinSymbols {
export const runtime_register = "~lib/util/runtime/register";
export const runtime_discard = "~lib/util/runtime/discard";
export const runtime_makeArray = "~lib/util/runtime/makeArray";
export const gc_mark_roots = "~lib/runtime/__gc_mark_roots";
export const gc_mark_members = "~lib/runtime/__gc_mark_members";
// std/typedarray.ts
export const Int8Array = "~lib/typedarray/Int8Array";
@ -3630,7 +3632,7 @@ export function compileCall(
// === Internal runtime =======================================================================
case BuiltinSymbols.runtime_id: {
case BuiltinSymbols.idof: {
let type = evaluateConstantType(compiler, typeArguments, operands, reportNode);
compiler.currentType = Type.u32;
if (!type) return module.createUnreachable();
@ -3644,31 +3646,32 @@ export function compileCall(
}
return module.createI32(classReference.id);
}
case BuiltinSymbols.gc_mark_roots: {
case BuiltinSymbols.visit_globals: {
if (
checkTypeAbsent(typeArguments, reportNode, prototype) |
checkArgsRequired(operands, 0, reportNode, compiler)
) {
compiler.currentType = Type.void;
return module.createUnreachable();
}
compiler.needsGcMark = true;
compiler.currentType = Type.void;
return module.createCall(BuiltinSymbols.gc_mark_roots, null, NativeType.None);
}
case BuiltinSymbols.gc_mark_members: {
if (
checkTypeAbsent(typeArguments, reportNode, prototype) |
checkArgsRequired(operands, 2, reportNode, compiler)
checkArgsRequired(operands, 1, reportNode, compiler) // cookie
) {
compiler.currentType = Type.void;
return module.createUnreachable();
}
let arg0 = compiler.compileExpression(operands[0], Type.u32, ConversionKind.IMPLICIT, WrapMode.NONE);
let arg1 = compiler.compileExpression(operands[1], compiler.options.usizeType, ConversionKind.IMPLICIT, WrapMode.NONE);
compiler.needsGcMark = true;
compiler.needsVisitGlobals = true;
compiler.currentType = Type.void;
return module.createCall(BuiltinSymbols.gc_mark_members, [ arg0, arg1 ], NativeType.None);
return module.createCall(BuiltinSymbols.visit_globals, [ arg0 ], NativeType.None);
}
case BuiltinSymbols.visit_members: {
if (
checkTypeAbsent(typeArguments, reportNode, prototype) |
checkArgsRequired(operands, 2, reportNode, compiler) // ref, cookie
) {
compiler.currentType = Type.void;
return module.createUnreachable();
}
let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, ConversionKind.IMPLICIT, WrapMode.NONE);
let arg1 = compiler.compileExpression(operands[1], Type.u32, ConversionKind.IMPLICIT, WrapMode.NONE);
compiler.needsVisitMembers = true;
compiler.currentType = Type.void;
return module.createCall(BuiltinSymbols.visit_members, [ arg0, arg1 ], NativeType.None);
}
}
@ -4051,15 +4054,15 @@ export function compileAbort(
]);
}
/** Compiles the `__gc_mark_roots` function. */
export function compileMarkRoots(compiler: Compiler): void {
/** Compiles the `visit_globals` function. */
export function compileVisitGlobals(compiler: Compiler): void {
var module = compiler.module;
var exprs = new Array<ExpressionRef>();
var typeRef = compiler.ensureFunctionType(null, Type.void);
var typeRef = compiler.ensureFunctionType([ Type.u32 ], Type.void); // cookie
var nativeSizeType = compiler.options.nativeSizeType;
var markRef = assert(compiler.program.markRef);
var visitInstance = assert(compiler.program.visitInstance);
compiler.compileFunction(markRef);
compiler.compileFunction(visitInstance);
for (let element of compiler.program.elementsByName.values()) {
if (element.kind != ElementKind.GLOBAL) continue;
@ -4074,7 +4077,7 @@ export function compileMarkRoots(compiler: Compiler): void {
let value = global.constantIntegerValue;
if (i64_low(value) || i64_high(value)) {
exprs.push(
module.createCall(markRef.internalName, [
module.createCall(visitInstance.internalName, [
compiler.options.isWasm64
? module.createI64(i64_low(value), i64_high(value))
: module.createI32(i64_low(value))
@ -4084,35 +4087,35 @@ export function compileMarkRoots(compiler: Compiler): void {
} else {
exprs.push(
module.createIf(
module.createTeeLocal(
0,
module.createTeeLocal(1,
module.createGetGlobal(global.internalName, nativeSizeType)
),
module.createCall(markRef.internalName, [
module.createGetLocal(0, nativeSizeType)
module.createCall(visitInstance.internalName, [
module.createGetLocal(1, nativeSizeType), // tempRef != null
module.createGetLocal(0, NativeType.I32) // cookie
], NativeType.None)
)
);
}
}
}
module.addFunction(BuiltinSymbols.gc_mark_roots, typeRef, [ nativeSizeType ],
module.addFunction(BuiltinSymbols.visit_globals, typeRef, [ nativeSizeType ],
exprs.length
? module.createBlock(null, exprs)
: module.createNop()
);
}
/** Compiles the `__gc_mark_members` function. */
export function compileMarkMembers(compiler: Compiler): void {
/** Compiles the `visit_members` function. */
export function compileVisitMembers(compiler: Compiler): void {
var program = compiler.program;
var module = compiler.module;
var usizeType = program.options.usizeType;
var nativeSizeType = usizeType.toNativeType();
var nativeSizeSize = usizeType.byteSize;
var ftype = compiler.ensureFunctionType([ Type.i32, usizeType ], Type.void);
var ftype = compiler.ensureFunctionType([ usizeType, Type.i32 ], Type.void); // ref, cookie
var managedClasses = program.managedClasses;
var markRef = assert(program.markRef);
var visitInstance = assert(program.visitInstance);
var names: string[] = [ "invalid" ]; // classId=0 is invalid
var blocks = new Array<ExpressionRef[]>();
var lastId = 0;
@ -4136,7 +4139,7 @@ export function compileMarkMembers(compiler: Compiler): void {
}
blocks.push([
module.createCall(traverseFunc.internalName, [
module.createGetLocal(1, nativeSizeType)
module.createGetLocal(0, nativeSizeType)
], NativeType.None),
module.createReturn()
]);
@ -4160,19 +4163,16 @@ export function compileMarkMembers(compiler: Compiler): void {
// if ($2 = value) FIELDCLASS~traverse($2)
module.createIf(
module.createTeeLocal(2,
module.createLoad(
nativeSizeSize,
false,
module.createGetLocal(1, nativeSizeType),
nativeSizeType,
fieldOffset
module.createLoad(nativeSizeSize, false,
module.createGetLocal(0, nativeSizeType),
nativeSizeType, fieldOffset
)
),
module.createBlock(null, [
module.createCall(markRef.internalName, [
module.createCall(visitInstance.internalName, [
module.createGetLocal(2, nativeSizeType)
], NativeType.None),
module.createCall(BuiltinSymbols.gc_mark_members, [
module.createCall(BuiltinSymbols.visit_members, [
module.createI32(fieldClassId),
module.createGetLocal(2, nativeSizeType)
], NativeType.None)
@ -4191,15 +4191,29 @@ export function compileMarkMembers(compiler: Compiler): void {
var current: ExpressionRef;
if (blocks.length) {
// create a big switch mapping class ids to traversal logic
// create a big switch mapping runtime ids to traversal functions
current = module.createBlock(names[1], [
module.createSwitch(names, "invalid", module.createGetLocal(0, NativeType.I32))
module.createSwitch(names, "invalid",
module.createLoad(nativeSizeSize, false,
nativeSizeType == NativeType.I64
? module.createBinary(BinaryOp.SubI64,
module.createGetLocal(0, nativeSizeType),
module.createI64(8)
)
: module.createBinary(BinaryOp.SubI32,
module.createGetLocal(0, nativeSizeType),
module.createI32(8) // rtId is at -8
),
NativeType.I32,
0
)
)
]);
for (let i = 0, k = blocks.length; i < k; ++i) {
blocks[i].unshift(current);
current = module.createBlock(i == k - 1 ? "invalid" : names[i + 2], blocks[i]);
}
compiler.compileFunction(markRef);
compiler.compileFunction(visitInstance);
// wrap the function with a terminating unreachable
current = module.createBlock(null, [
current,
@ -4209,7 +4223,7 @@ export function compileMarkMembers(compiler: Compiler): void {
// simplify
current = module.createUnreachable();
}
module.addFunction(BuiltinSymbols.gc_mark_members, ftype, [ nativeSizeType ], current);
module.addFunction(BuiltinSymbols.visit_members, ftype, [ nativeSizeType ], current);
}
function typeToRuntimeFlags(type: Type, program: Program): RTTIFlags {

View File

@ -182,13 +182,17 @@ export namespace CommonSymbols {
export const abort = "abort";
export const pow = "pow";
export const mod = "mod";
export const allocate = "allocate";
export const reallocate = "reallocate";
export const register = "register";
export const discard = "discard";
export const newString = "newString";
export const newArrayBuffer = "newArrayBuffer";
export const newArray = "newArray";
export const alloc = "__alloc";
export const realloc = "__realloc";
export const free = "__free";
export const retain = "__retain";
export const release = "__release";
export const retainRelease = "__retainRelease";
export const collect = "__collect";
export const typeinfo = "__typeinfo";
export const instanceof_ = "__instanceof";
export const visit = "__visit";
export const allocArray = "__allocArray";
}
// shared

View File

@ -7,8 +7,8 @@ import {
BuiltinSymbols,
compileCall as compileBuiltinCall,
compileAbort,
compileMarkRoots,
compileMarkMembers,
compileVisitGlobals,
compileVisitMembers,
compileRTTI,
} from "./builtins";
@ -281,8 +281,10 @@ export class Compiler extends DiagnosticEmitter {
argcSet: FunctionRef = 0;
/** Whether HEAP_BASE is required. */
needsHeap: bool = false;
/** Indicates whether the __gc_mark_* functions must be generated. */
needsGcMark: bool = false;
/** Indicates whether the __visit_globals function must be generated. */
needsVisitGlobals: bool = false;
/** Indicated whether the __visit_members function must be generated. */
needsVisitMembers: bool = false;
/** Whether RTTI is required. */
needsRTTI: bool = false;
@ -359,10 +361,8 @@ export class Compiler extends DiagnosticEmitter {
}
// compile gc features if utilized
if (this.needsGcMark) {
compileMarkRoots(this);
compileMarkMembers(this);
}
if (this.needsVisitGlobals) compileVisitGlobals(this);
if (this.needsVisitMembers) compileVisitMembers(this);
// compile runtime type information
module.removeGlobal(BuiltinSymbols.RTTI_BASE);
@ -593,71 +593,32 @@ export class Compiler extends DiagnosticEmitter {
var nativeType = type.toNativeType();
var usizeType = this.options.usizeType;
var nativeSizeType = usizeType.toNativeType();
var valueExpr: ExpressionRef;
if (type.isManaged(program)) {
let fn1: Function | null, fn2: Function | null;
let body: ExpressionRef[] = [];
if (fn1 = program.linkRef) { // tracing
if (fn2 = program.unlinkRef) {
body.push(
module.createCall(fn2.internalName, [
module.createGetLocal(2, nativeType),
module.createGetLocal(0, nativeSizeType)
], NativeType.None)
);
}
body.push(
module.createCall(fn1.internalName, [
module.createGetLocal(1, nativeSizeType),
module.createGetLocal(0, nativeSizeType)
], NativeType.None)
);
} else if (fn1 = program.retainRef) { // arc
fn2 = assert(program.releaseRef);
body.push(
module.createCall(fn2.internalName, [
module.createGetLocal(2, nativeType)
], NativeType.None)
);
body.push(
module.createCall(fn1.internalName, [
module.createGetLocal(1, nativeSizeType)
], NativeType.None)
);
}
module.addFunction(
name,
this.ensureFunctionType([ type ], Type.void, usizeType),
[ nativeType ],
module.createIf( // if (value != oldValue) release/retain ..
module.createBinary(
nativeSizeType == NativeType.I64
? BinaryOp.NeI64
: BinaryOp.NeI32,
module.createGetLocal(1, nativeType),
module.createTeeLocal(2,
module.createLoad(type.byteSize, false,
module.createGetLocal(0, nativeSizeType),
nativeType, field.memoryOffset
)
)
),
module.createBlock(null, body)
)
);
} else {
module.addFunction(
name,
this.ensureFunctionType([ type ], Type.void, usizeType),
null,
module.createStore(
type.byteSize,
let retainReleaseInstance = program.retainReleaseInstance;
this.compileFunction(retainReleaseInstance);
valueExpr = module.createCall(retainReleaseInstance.internalName, [
module.createGetLocal(1, nativeType), // newRef
module.createLoad(type.byteSize, false, // oldRef
module.createGetLocal(0, nativeSizeType),
module.createGetLocal(1, nativeType),
nativeType,
field.memoryOffset
nativeType, field.memoryOffset
)
);
], nativeType);
} else {
valueExpr = module.createGetLocal(1, nativeType);
}
module.addFunction(
name,
this.ensureFunctionType([ type ], Type.void, usizeType),
null,
module.createStore(
type.byteSize,
module.createGetLocal(0, nativeSizeType),
valueExpr,
nativeType,
field.memoryOffset
)
);
module.addFunctionExport(name, name);
}
@ -969,8 +930,11 @@ export class Compiler extends DiagnosticEmitter {
);
}
module.addGlobal(internalName, nativeType, true, type.toNativeZero(module));
if (type.isManaged(this.program) && this.program.retainRef) {
initExpr = this.makeInsertRef(initExpr, null, type.is(TypeFlags.NULLABLE));
let program = this.program;
if (type.isManaged(program)) {
let retainInstance = program.retainInstance;
this.compileFunction(retainInstance);
initExpr = module.createCall(retainInstance.internalName, [ initExpr ], nativeType);
}
this.currentBody.push(
module.createSetGlobal(internalName, initExpr)
@ -5230,75 +5194,124 @@ export class Compiler extends DiagnosticEmitter {
return module.createUnreachable();
}
/** Makes an assignment to a local, possibly retaining and releasing affected references and keeping track of wrap and null states. */
makeLocalAssignment(
local: Local,
valueExpr: ExpressionRef,
tee: bool,
possiblyNull: bool
): ExpressionRef {
// TBD: use REPLACE macro to keep track of managed refcounts? or can the compiler evaluate
// this statically in closed contexts like functions in order to safe the extra work?
var module = this.module;
var program = this.program;
var type = local.type;
assert(type != Type.void);
var nativeType = type.toNativeType();
var flow = this.currentFlow;
var localIndex = local.index;
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
if (!flow.canOverflow(valueExpr, type)) flow.setLocalFlag(localIndex, LocalFlags.WRAPPED);
else flow.unsetLocalFlag(localIndex, LocalFlags.WRAPPED);
}
if (type.is(TypeFlags.NULLABLE)) {
if (possiblyNull) flow.unsetLocalFlag(localIndex, LocalFlags.NONNULL);
else flow.setLocalFlag(localIndex, LocalFlags.NONNULL);
}
if (tee) {
this.currentType = type;
return this.module.createTeeLocal(localIndex, valueExpr);
// TODO: retain/release on each local assignment is costly in that increments and decrements
// easily involve cache misses when updating refcounts. ultimate goal should be to statically
// eliminate as many retain/release calls on locals as possible, i.e. where it can be proven
// that refcount doesn't change during the execution of a function, respectively refcount on
// arguments (which are locals) can be proven to remain the same from pre-call to post-call.
if (type.isManaged(program)) {
let retainReleaseInstance = program.retainReleaseInstance;
this.compileFunction(retainReleaseInstance);
if (tee) { // TEE(local = __retainRelease(value, local))
this.currentType = type;
return module.createTeeLocal(localIndex,
module.createCall(retainReleaseInstance.internalName, [
valueExpr,
module.createGetLocal(localIndex, nativeType)
], nativeType)
);
} else { // local = __retainRelease(value, local)
this.currentType = Type.void;
return module.createSetLocal(localIndex,
module.createCall(retainReleaseInstance.internalName, [
valueExpr,
module.createGetLocal(localIndex, nativeType)
], nativeType)
);
}
} else {
this.currentType = Type.void;
return this.module.createSetLocal(localIndex, valueExpr);
if (tee) { // TEE(local = value)
this.currentType = type;
return this.module.createTeeLocal(localIndex, valueExpr);
} else { // local = value
this.currentType = Type.void;
return this.module.createSetLocal(localIndex, valueExpr);
}
}
}
/** Makes an assignment to a global, possibly retaining and releasing affected references. */
makeGlobalAssignment(global: Global, valueExpr: ExpressionRef, tee: bool): ExpressionRef {
var module = this.module;
var program = this.program;
var type = global.type;
assert(type != Type.void);
var nativeType = type.toNativeType();
// MANAGED (reference counting)
if (type.isManaged(this.program)) {
if (this.program.retainRef) {
valueExpr = this.makeReplaceRef(
valueExpr,
module.createGetGlobal(global.internalName, nativeType),
null,
type.is(TypeFlags.NULLABLE)
if (type.isManaged(program)) {
let retainReleaseInstance = program.retainReleaseInstance;
this.compileFunction(retainReleaseInstance);
if (tee) { // (global = __retainRelease(t1 = value, global)), t1
let tempValue = this.currentFlow.getAndFreeTempLocal(type, true); // globals are wrapped
this.currentType = type;
return module.createBlock(null, [
module.createSetGlobal(global.internalName,
module.createCall(retainReleaseInstance.internalName, [
module.createTeeLocal(tempValue.index, valueExpr),
module.createGetGlobal(global.internalName, nativeType)
], nativeType)
),
module.createGetLocal(tempValue.index, nativeType)
], nativeType);
} else { // global = __retainRelease(value, global)
this.currentType = Type.void;
return module.createSetGlobal(global.internalName,
module.createCall(retainReleaseInstance.internalName, [
valueExpr,
module.createGetGlobal(global.internalName, nativeType)
], nativeType)
);
}
// UNMANAGED
} else {
valueExpr = this.ensureSmallIntegerWrap(valueExpr, type); // global values must be wrapped
}
if (tee) {
let tempValue = this.currentFlow.getAndFreeTempLocal(type, true);
this.currentType = type;
return module.createBlock(null, [
module.createSetGlobal(global.internalName,
module.createTeeLocal(tempValue.index, valueExpr)
),
module.createGetLocal(tempValue.index, nativeType)
], nativeType);
} else {
this.currentType = Type.void;
return this.module.createSetGlobal(global.internalName, valueExpr);
valueExpr = this.ensureSmallIntegerWrap(valueExpr, type); // globals must be wrapped
if (tee) { // (global = (t1 = value)), t1
let tempValue = this.currentFlow.getAndFreeTempLocal(type, true);
this.currentType = type;
return module.createBlock(null, [
module.createSetGlobal(global.internalName,
module.createTeeLocal(tempValue.index, valueExpr)
),
module.createGetLocal(tempValue.index, nativeType)
], nativeType);
} else { // global = value
this.currentType = Type.void;
return module.createSetGlobal(global.internalName,
valueExpr
);
}
}
}
/** Makes an assignment to a field, possibly retaining and releasing affected references. */
makeFieldAssignment(field: Field, valueExpr: ExpressionRef, thisExpr: ExpressionRef, tee: bool): ExpressionRef {
var program = this.program;
var module = this.module;
var program = this.program;
var flow = this.currentFlow;
var fieldType = field.type;
var nativeFieldType = fieldType.toNativeType();
@ -5306,69 +5319,63 @@ export class Compiler extends DiagnosticEmitter {
var thisType = (<Class>field.parent).type;
var nativeThisType = thisType.toNativeType();
// MANAGED: this.field = replace(value, this.field)
if (fieldType.isManaged(program)) {
if (fieldType.isManaged(program) && thisType.isManaged(program)) {
let tempThis = flow.getTempLocal(thisType, false);
let expr: ExpressionRef;
if (tee) { // tee value to a temp local and make it the block's result
let tempValue = flow.getTempLocal(fieldType, !flow.canOverflow(valueExpr, fieldType));
expr = module.createBlock(null, [
let retainReleaseInstance = program.retainReleaseInstance;
this.compileFunction(retainReleaseInstance);
if (tee) { // ((t1 = this).field = __retainRelease(t2 = value, t1.field)), t2
let tempValue = flow.getAndFreeTempLocal(fieldType, !flow.canOverflow(valueExpr, fieldType));
flow.freeTempLocal(tempThis);
this.currentType = fieldType;
return module.createBlock(null, [
module.createStore(fieldType.byteSize,
module.createTeeLocal(tempThis.index, thisExpr),
this.makeReplaceRef(
module.createTeeLocal(tempValue.index, valueExpr),
module.createLoad(fieldType.byteSize, fieldType.is(TypeFlags.SIGNED),
module.createCall(retainReleaseInstance.internalName, [
module.createTeeLocal(tempValue.index, valueExpr), // newRef
module.createLoad(fieldType.byteSize, fieldType.is(TypeFlags.SIGNED), // oldRef
module.createGetLocal(tempThis.index, nativeThisType),
nativeFieldType, field.memoryOffset
),
tempThis,
fieldType.is(TypeFlags.NULLABLE)
),
)
], nativeFieldType),
nativeFieldType, field.memoryOffset
),
module.createGetLocal(tempValue.index, nativeFieldType)
], nativeFieldType);
flow.freeTempLocal(tempValue);
this.currentType = fieldType;
} else { // no need for a temp local
expr = module.createStore(fieldType.byteSize,
} else { // (t1 = this).field = __retainRelease(value, t1.field)
flow.freeTempLocal(tempThis);
this.currentType = Type.void;
return module.createStore(fieldType.byteSize,
module.createTeeLocal(tempThis.index, thisExpr),
this.makeReplaceRef(
valueExpr,
module.createLoad(fieldType.byteSize, fieldType.is(TypeFlags.SIGNED),
module.createCall(retainReleaseInstance.internalName, [
valueExpr, // newRef
module.createLoad(fieldType.byteSize, fieldType.is(TypeFlags.SIGNED), // oldRef
module.createGetLocal(tempThis.index, nativeThisType),
nativeFieldType, field.memoryOffset
),
tempThis,
fieldType.is(TypeFlags.NULLABLE)
),
)
], nativeFieldType),
nativeFieldType, field.memoryOffset
);
this.currentType = Type.void;
}
flow.freeTempLocal(tempThis);
return expr;
}
// UNMANAGED: this.field = value
if (tee) {
this.currentType = fieldType;
let tempValue = flow.getAndFreeTempLocal(fieldType, !flow.canOverflow(valueExpr, fieldType));
return module.createBlock(null, [
module.createStore(fieldType.byteSize,
thisExpr,
module.createTeeLocal(tempValue.index, valueExpr),
nativeFieldType, field.memoryOffset
),
module.createGetLocal(tempValue.index, nativeFieldType)
], nativeFieldType);
} else {
this.currentType = Type.void;
return module.createStore(fieldType.byteSize,
thisExpr,
valueExpr,
nativeFieldType, field.memoryOffset
);
if (tee) { // (this.field = (t1 = value)), t1
let tempValue = flow.getAndFreeTempLocal(fieldType, !flow.canOverflow(valueExpr, fieldType));
this.currentType = fieldType;
return module.createBlock(null, [
module.createStore(fieldType.byteSize,
thisExpr,
module.createTeeLocal(tempValue.index, valueExpr),
nativeFieldType, field.memoryOffset
),
module.createGetLocal(tempValue.index, nativeFieldType)
], nativeFieldType);
} else { // this.field = value
this.currentType = Type.void;
return module.createStore(fieldType.byteSize,
thisExpr,
valueExpr,
nativeFieldType, field.memoryOffset
)
}
}
}
@ -7015,7 +7022,7 @@ export class Compiler extends DiagnosticEmitter {
// otherwise allocate a new array header and make it wrap a copy of the static buffer
} else {
// makeArray(length, alignLog2, classId, staticBuffer)
let expr = this.makeCallDirect(assert(program.makeArrayInstance), [
let expr = this.makeCallDirect(program.allocArrayInstance, [
module.createI32(length),
program.options.isWasm64
? module.createI64(elementType.alignLog2)
@ -7044,12 +7051,11 @@ export class Compiler extends DiagnosticEmitter {
var flow = this.currentFlow;
var tempThis = flow.getTempLocal(arrayType, false);
var tempDataStart = flow.getTempLocal(arrayBufferInstance.type);
var newArrayInstance = assert(program.makeArrayInstance);
var stmts = new Array<ExpressionRef>();
// tempThis = makeArray(length, alignLog2, classId, source = 0)
stmts.push(
module.createSetLocal(tempThis.index,
this.makeCallDirect(newArrayInstance, [
this.makeCallDirect(program.allocArrayInstance, [
module.createI32(length),
program.options.isWasm64
? module.createI64(elementType.alignLog2)
@ -7080,12 +7086,10 @@ export class Compiler extends DiagnosticEmitter {
? this.compileExpression(valueExpression, elementType, ConversionKind.IMPLICIT, WrapMode.NONE)
: elementType.toNativeZero(module);
if (isManaged) {
// value = link/retain(value[, tempThis])
valueExpr = this.makeInsertRef(
valueExpr,
tempThis,
elementType.is(TypeFlags.NULLABLE)
);
// value = __retain(value)
valueExpr = this.makeCallDirect(program.retainInstance, [
valueExpr
], reportNode);
}
// store<T>(tempData, value, immOffset)
stmts.push(
@ -8290,35 +8294,17 @@ export class Compiler extends DiagnosticEmitter {
var module = this.module;
var options = this.options;
var classType = classInstance.type;
if (!program.allocateMem) {
this.error(
DiagnosticCode.An_allocator_must_be_present_to_use_0,
reportNode.range, "new"
);
}
if (classInstance.hasDecorator(DecoratorFlags.UNMANAGED)) {
// memory.allocate(sizeof<T>())
this.currentType = classType;
return this.makeCallDirect(assert(program.memoryAllocateInstance), [
options.isWasm64
? module.createI64(classInstance.currentMemoryOffset)
: module.createI32(classInstance.currentMemoryOffset)
], reportNode);
} else {
// register(allocate(sizeof<T>()), classId)
this.currentType = classType;
return this.makeCallDirect(assert(program.registerInstance), [
this.makeCallDirect(assert(program.allocateInstance), [
options.isWasm64
? module.createI64(classInstance.currentMemoryOffset)
: module.createI32(classInstance.currentMemoryOffset)
], reportNode),
module.createI32(classInstance.id)
], reportNode);
}
this.currentType = classType;
return this.makeCallDirect(program.allocInstance, [
options.isWasm64
? module.createI64(classInstance.currentMemoryOffset)
: module.createI32(classInstance.currentMemoryOffset),
module.createI32(
classInstance.hasDecorator(DecoratorFlags.UNMANAGED)
? 0
: classInstance.id
)
], reportNode);
}
/** Makes the initializers for a class's fields. */
@ -8383,182 +8369,206 @@ export class Compiler extends DiagnosticEmitter {
return stmts;
}
/** Wraps a reference in a `retain` call. Returns the reference if `tempLocal` is specified. */
makeRetain(valueExpr: ExpressionRef, tempIndex: i32 = -1): ExpressionRef {
var module = this.module;
var program = this.program;
var retainFn = assert(program.retainRef);
this.compileFunction(retainFn);
if (tempIndex >= 0) {
let nativeSizeType = this.options.nativeSizeType;
return module.createBlock(null, [
module.createCall(retainFn.internalName, [
module.createTeeLocal(tempIndex, valueExpr)
], NativeType.None),
module.createGetLocal(tempIndex, nativeSizeType)
], nativeSizeType);
} else {
return module.createCall(retainFn.internalName, [ valueExpr ], NativeType.None);
}
}
// private makeRetainOrRelease(fn: Function, expr: ExpressionRef, possiblyNull: bool, tee: bool, tempIndex: i32 = -1): ExpressionRef {
// var module = this.module;
// var nativeSizeType = this.options.nativeSizeType;
// if (tee) {
// if (possiblyNull) {
// assert(tempIndex >= 0);
// return module.createBlock(null, [
// module.createIf(
// module.createTeeLocal(tempIndex, expr),
// module.createCall(fn.internalName, [
// module.createGetLocal(tempIndex, nativeSizeType)
// ], NativeType.None)
// ),
// module.createGetLocal(tempIndex, nativeSizeType)
// ], nativeSizeType);
// } else {
// assert(tempIndex >= 0);
// return module.createBlock(null, [
// module.createCall(fn.internalName, [
// module.createTeeLocal(tempIndex, expr)
// ], NativeType.None),
// module.createGetLocal(tempIndex, nativeSizeType)
// ], nativeSizeType);
// }
// } else {
// if (possiblyNull) {
// assert(tempIndex >= 0);
// return module.createIf(
// module.createTeeLocal(tempIndex, expr),
// module.createCall(fn.internalName, [
// module.createGetLocal(tempIndex, nativeSizeType)
// ], NativeType.None)
// );
// } else {
// return module.createCall(fn.internalName, [ expr ], NativeType.None);
// }
// }
// }
/** Wraps a reference in `release` call. Returns the reference if `tempLocal` is specified. */
makeRelease(valueExpr: ExpressionRef, tempIndex: i32 = -1): ExpressionRef {
var module = this.module;
var program = this.program;
var releaseFn = assert(program.releaseRef);
this.compileFunction(releaseFn);
if (tempIndex >= 0) {
let nativeSizeType = this.options.nativeSizeType;
return module.createBlock(null, [
module.createCall(releaseFn.internalName, [
module.createTeeLocal(tempIndex, valueExpr)
], NativeType.None),
module.createGetLocal(tempIndex, nativeSizeType)
], nativeSizeType);
} else {
return module.createCall(releaseFn.internalName, [ valueExpr ], NativeType.None);
}
}
// /** Wraps an expression of a reference type in a `retain` call. */
// makeRetain(expr: ExpressionRef, possiblyNull: bool, tee: bool, tempIndex: i32 = -1): ExpressionRef {
// return this.makeRetainOrRelease(this.program.retainInstance, expr, possiblyNull, tee, tempIndex);
// }
/** Wraps a new and an old reference in a sequence of `retain` and `release` calls. */
makeRetainRelease(newValueExpr: ExpressionRef, oldValueExpr: ExpressionRef, tempIndex: i32 = -1): ExpressionRef {
// TODO: checking `newValue != oldValue` significantly reduces strain on the roots buffer
// when cyclic structures may be immediately released but also requires a tempIndex. might
// be worth to require a temp here. furthermore it might be worth to require it for retain
// and release as well so we can emit != null checks where necessary only?
var module = this.module;
if (tempIndex >= 0) {
let nativeSizeType = this.options.nativeSizeType;
return module.createBlock(null, [
this.makeRetain(module.createTeeLocal(tempIndex, newValueExpr)),
this.makeRelease(oldValueExpr),
module.createGetLocal(tempIndex, nativeSizeType)
], nativeSizeType);
} else {
return module.createBlock(null, [
this.makeRetain(newValueExpr),
this.makeRelease(oldValueExpr)
]);
}
}
// /** Wraps an expression of a reference type in a `release` call. */
// makeRelease(expr: ExpressionRef, possiblyNull: bool, tee: bool, tempIndex: i32 = -1): ExpressionRef {
// return this.makeRetainOrRelease(this.program.releaseInstance, expr, possiblyNull, tee, tempIndex);
// }
/** Prepares the insertion of a reference into an _uninitialized_ parent using the GC interface. */
makeInsertRef(
valueExpr: ExpressionRef,
tempParent: Local | null,
nullable: bool
): ExpressionRef {
var module = this.module;
var program = this.program;
var usizeType = this.options.usizeType;
var nativeSizeType = this.options.nativeSizeType;
var flow = this.currentFlow;
var tempValue = flow.getTempLocal(usizeType, false);
var handle: ExpressionRef;
var fn: Function | null;
if (fn = program.linkRef) { // tracing
handle = module.createCall(fn.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType),
module.createGetLocal(assert(tempParent).index, nativeSizeType)
], NativeType.None);
} else if (fn = program.retainRef) { // arc
handle = module.createCall(fn.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType)
], NativeType.None);
} else {
assert(false);
return module.createUnreachable();
}
flow.freeTempLocal(tempValue);
if (!this.compileFunction(fn)) return module.createUnreachable();
// {
// [if (value !== null)] link/retain(value[, parent])
// } -> value
return module.createBlock(null, [
module.createSetLocal(tempValue.index, valueExpr),
nullable
? module.createIf(
module.createGetLocal(tempValue.index, nativeSizeType),
handle
)
: handle,
module.createGetLocal(tempValue.index, nativeSizeType)
], nativeSizeType);
}
// /** Wraps a new and an old expression of a reference type in a `retain` call for the new and a `release` call for the old expression. */
// makeRetainRelease(newExpr: ExpressionRef, oldExpr: ExpressionRef, possiblyNull: bool, tempIndexNew: i32, tempIndexOld: i32): ExpressionRef {
// var module = this.module;
// var nativeSizeType = this.options.nativeSizeType;
// return module.createIf(
// module.createBinary(
// nativeSizeType == NativeType.I32
// ? BinaryOp.NeI32
// : BinaryOp.NeI64,
// module.createTeeLocal(tempIndexNew, newExpr),
// module.createTeeLocal(tempIndexOld, oldExpr)
// ),
// module.createBlock(null, [
// this.makeRetain(module.createGetLocal(tempIndexNew, nativeSizeType), possiblyNull, false, tempIndexNew),
/** Prepares the replaces a reference hold by an _initialized_ parent using the GC interface. */
makeReplaceRef(
valueExpr: ExpressionRef,
oldValueExpr: ExpressionRef,
tempParent: Local | null,
nullable: bool
): ExpressionRef {
var module = this.module;
var program = this.program;
var usizeType = this.options.usizeType;
var nativeSizeType = this.options.nativeSizeType;
var flow = this.currentFlow;
var tempValue = flow.getTempLocal(usizeType, false);
var tempOldValue = flow.getTempLocal(usizeType, false);
var handleOld: ExpressionRef = 0;
var handleNew: ExpressionRef;
var fn1: Function | null, fn2: Function | null;
if (fn1 = program.linkRef) { // tracing
tempParent = assert(tempParent);
if (fn2 = program.unlinkRef) {
handleOld = module.createCall(fn2.internalName, [
module.createGetLocal(tempOldValue.index, nativeSizeType),
module.createGetLocal(tempParent.index, nativeSizeType)
], NativeType.None);
}
handleNew = module.createCall(fn1.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType),
module.createGetLocal(tempParent.index, nativeSizeType)
], NativeType.None);
} else if (fn1 = program.retainRef) { // arc
fn2 = assert(program.releaseRef);
handleOld = module.createCall(fn2.internalName, [
module.createGetLocal(tempOldValue.index, nativeSizeType)
], NativeType.None);
handleNew = module.createCall(fn1.internalName, [
module.createGetLocal(tempValue.index, nativeSizeType)
], NativeType.None);
} else {
assert(false);
return module.createUnreachable();
}
flow.freeTempLocal(tempValue);
flow.freeTempLocal(tempOldValue);
if (!this.compileFunction(fn1)) return module.createUnreachable();
if (fn2 && !this.compileFunction(fn2)) return module.createUnreachable();
// if (value != oldValue) {
// if (oldValue !== null) unlink/release(oldValue[, parent])
// [if (value !== null)] link/retain(value[, parent])
// } -> value
return module.createIf(
module.createBinary(nativeSizeType == NativeType.I32 ? BinaryOp.NeI32 : BinaryOp.NeI64,
module.createTeeLocal(tempValue.index, valueExpr),
module.createTeeLocal(tempOldValue.index, oldValueExpr)
),
module.createBlock(null, [
handleOld
? module.createIf(
module.createGetLocal(tempOldValue.index, nativeSizeType),
handleOld
)
: module.createNop(),
nullable
? module.createIf(
module.createGetLocal(tempValue.index, nativeSizeType),
handleNew
)
: handleNew,
module.createGetLocal(tempValue.index, nativeSizeType)
], nativeSizeType),
module.createGetLocal(tempValue.index, nativeSizeType)
);
}
// ], NativeType.None)
// )
// return module.createBlock(null, [
// this.makeRetain(newExpr, possiblyNull, true, tempIndex),
// this.makeRelease(oldExpr, possiblyNull, false), // wrong: reuses tempIndex if possiblyNull
// ], nativeSizeType);
// }
// /** Wraps a new and an old reference in a sequence of `retain` and `release` calls. */
// makeRetainRelease(newValueExpr: ExpressionRef, oldValueExpr: ExpressionRef, tempIndex: i32, possiblyNull: bool = true): ExpressionRef {
// var module = this.module;
// var nativeSizeType = this.options.nativeSizeType;
// return module.createBlock(null, [
// this.makeRetain(module.createTeeLocal(tempIndex, newValueExpr), possiblyNull ? tempIndex : -1),
// this.makeRelease(oldValueExpr, possiblyNull ? tempIndex : -1),
// module.createGetLocal(tempIndex, nativeSizeType)
// ], nativeSizeType);
// }
// /** Prepares the insertion of a reference into an _uninitialized_ parent using the GC interface. */
// makeInsertRef(
// valueExpr: ExpressionRef,
// tempParent: Local | null,
// nullable: bool
// ): ExpressionRef {
// var module = this.module;
// var program = this.program;
// var usizeType = this.options.usizeType;
// var nativeSizeType = this.options.nativeSizeType;
// var flow = this.currentFlow;
// var tempValue = flow.getTempLocal(usizeType, false);
// var handle: ExpressionRef;
// var fn: Function | null;
// if (fn = program.linkRef) { // tracing
// handle = module.createCall(fn.internalName, [
// module.createGetLocal(tempValue.index, nativeSizeType),
// module.createGetLocal(assert(tempParent).index, nativeSizeType)
// ], NativeType.None);
// } else if (fn = program.retainRef) { // arc
// handle = module.createCall(fn.internalName, [
// module.createGetLocal(tempValue.index, nativeSizeType)
// ], NativeType.None);
// } else {
// assert(false);
// return module.createUnreachable();
// }
// flow.freeTempLocal(tempValue);
// if (!this.compileFunction(fn)) return module.createUnreachable();
// // {
// // [if (value !== null)] link/retain(value[, parent])
// // } -> value
// return module.createBlock(null, [
// module.createSetLocal(tempValue.index, valueExpr),
// nullable
// ? module.createIf(
// module.createGetLocal(tempValue.index, nativeSizeType),
// handle
// )
// : handle,
// module.createGetLocal(tempValue.index, nativeSizeType)
// ], nativeSizeType);
// }
// /** Prepares the replaces a reference hold by an _initialized_ parent using the GC interface. */
// makeReplaceRef(
// valueExpr: ExpressionRef,
// oldValueExpr: ExpressionRef,
// tempParent: Local | null,
// nullable: bool
// ): ExpressionRef {
// var module = this.module;
// var program = this.program;
// var usizeType = this.options.usizeType;
// var nativeSizeType = this.options.nativeSizeType;
// var flow = this.currentFlow;
// var tempValue = flow.getTempLocal(usizeType, false);
// var tempOldValue = flow.getTempLocal(usizeType, false);
// var handleOld: ExpressionRef = 0;
// var handleNew: ExpressionRef;
// var fn1: Function | null, fn2: Function | null;
// if (fn1 = program.linkRef) { // tracing
// tempParent = assert(tempParent);
// if (fn2 = program.unlinkRef) {
// handleOld = module.createCall(fn2.internalName, [
// module.createGetLocal(tempOldValue.index, nativeSizeType),
// module.createGetLocal(tempParent.index, nativeSizeType)
// ], NativeType.None);
// }
// handleNew = module.createCall(fn1.internalName, [
// module.createGetLocal(tempValue.index, nativeSizeType),
// module.createGetLocal(tempParent.index, nativeSizeType)
// ], NativeType.None);
// } else if (fn1 = program.retainRef) { // arc
// fn2 = assert(program.releaseRef);
// handleOld = module.createCall(fn2.internalName, [
// module.createGetLocal(tempOldValue.index, nativeSizeType)
// ], NativeType.None);
// handleNew = module.createCall(fn1.internalName, [
// module.createGetLocal(tempValue.index, nativeSizeType)
// ], NativeType.None);
// } else {
// assert(false);
// return module.createUnreachable();
// }
// flow.freeTempLocal(tempValue);
// flow.freeTempLocal(tempOldValue);
// if (!this.compileFunction(fn1)) return module.createUnreachable();
// if (fn2 && !this.compileFunction(fn2)) return module.createUnreachable();
// // if (value != oldValue) {
// // if (oldValue !== null) unlink/release(oldValue[, parent])
// // [if (value !== null)] link/retain(value[, parent])
// // } -> value
// return module.createIf(
// module.createBinary(nativeSizeType == NativeType.I32 ? BinaryOp.NeI32 : BinaryOp.NeI64,
// module.createTeeLocal(tempValue.index, valueExpr),
// module.createTeeLocal(tempOldValue.index, oldValueExpr)
// ),
// module.createBlock(null, [
// handleOld
// ? module.createIf(
// module.createGetLocal(tempOldValue.index, nativeSizeType),
// handleOld
// )
// : module.createNop(),
// nullable
// ? module.createIf(
// module.createGetLocal(tempValue.index, nativeSizeType),
// handleNew
// )
// : handleNew,
// module.createGetLocal(tempValue.index, nativeSizeType)
// ], nativeSizeType),
// module.createGetLocal(tempValue.index, nativeSizeType)
// );
// }
makeInstanceOfClass(
expr: ExpressionRef,

View File

@ -349,58 +349,49 @@ export class Program extends DiagnosticEmitter {
/** Managed classes contained in the program, by id. */
managedClasses: Map<i32,Class> = new Map();
// runtime references
// standard references
/** ArrayBufferView reference. */
arrayBufferViewInstance: Class | null = null;
arrayBufferViewInstance: Class;
/** ArrayBuffer instance reference. */
arrayBufferInstance: Class | null = null;
arrayBufferInstance: Class;
/** Array prototype reference. */
arrayPrototype: ClassPrototype | null = null;
arrayPrototype: ClassPrototype;
/** Set prototype reference. */
setPrototype: ClassPrototype | null = null;
setPrototype: ClassPrototype;
/** Map prototype reference. */
mapPrototype: ClassPrototype | null = null;
mapPrototype: ClassPrototype;
/** Fixed array prototype reference. */
fixedArrayPrototype: ClassPrototype | null = null;
fixedArrayPrototype: ClassPrototype;
/** String instance reference. */
stringInstance: Class | null = null;
stringInstance: Class;
/** Abort function reference, if present. */
abortInstance: Function | null = null;
abortInstance: Function;
/** Runtime allocation function. `allocate(payloadSize: usize): usize` */
allocateInstance: Function | null = null;
/** Memory allocation function. `memory.allocate(size)` */
memoryAllocateInstance: Function | null = null;
/** Runtime reallocation function. `reallocate(ref: usize, newPayloadSize: usize): usize` */
reallocateInstance: Function | null = null;
/** Runtime discard function. `discard(ref: usize): void` */
discardInstance: Function | null = null;
/** Runtime register function. `register(ref: usize, cid: u32): usize` */
registerInstance: Function | null = null;
/** Runtime make array function. `newArray(length: i32, alignLog2: usize, id: u32, source: usize = 0): usize` */
makeArrayInstance: Function | null = null;
/** Runtime instanceof function. */
instanceofInstance: Function | null = null;
/** Runtime flags function. */
flagsInstance: Function | null = null;
// runtime references
/** The kind of garbage collector being present. */
collectorKind: CollectorKind = CollectorKind.NONE;
/** Memory allocation implementation, if present: `__mem_allocate(size: usize): usize` */
allocateMem: Function | null = null;
/** Memory free implementation, if present: `__mem_free(ref: usize): void` */
freeMem: Function | null = null;
/** Reference link implementation, if present: `__ref_link(ref: usize, parentRef: usize): void` */
linkRef: Function | null = null;
/** Reference unlink implementation, if present: `__ref_unlink(ref: usize, parentRef: usize): void` */
unlinkRef: Function | null = null;
/** Reference retain implementation, if present: `__ref_retain(ref: usize): void` */
retainRef: Function | null = null;
/** Reference release implementation, if present: `__ref_release(ref: usize): void` */
releaseRef: Function | null = null;
/** Reference mark implementation, if present: `__ref_mark(ref: usize): void` */
markRef: Function | null = null;
/** RT `__alloc(size: usize, id: u32): usize` */
allocInstance: Function;
/** RT `__realloc(ref: usize, newSize: usize): usize` */
reallocInstance: Function;
/** RT `__free(ref: usize): void` */
freeInstance: Function;
/** RT `__retain(ref: usize): usize` */
retainInstance: Function;
/** RT `__release(ref: usize): void` */
releaseInstance: Function;
/** RT `__retainRelease(newRef: usize, oldRef: usize): usize` */
retainReleaseInstance: Function;
/** RT `__collect(): void` */
collectInstance: Function;
/** RT `__visit(ref: usize, cookie: u32): void` */
visitInstance: Function;
/** RT `__typeinfo(id: u32): RTTIFlags` */
typeinfoInstance: Function;
/** RT `__instanceof(ref: usize, superId: u32): bool` */
instanceofInstance: Function;
/** RT `__allocArray(length: i32, alignLog2: usize, id: u32, data: usize = 0): usize` */
allocArrayInstance: Function;
/** Next class id. */
nextClassId: u32 = 1;
@ -805,104 +796,26 @@ export class Program extends DiagnosticEmitter {
}
}
// register library elements
{
let element: Element | null;
if (element = this.lookupGlobal(CommonSymbols.ArrayBufferView)) {
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.arrayBufferViewInstance = resolver.resolveClass(<ClassPrototype>element, null);
}
if (element = this.lookupGlobal(CommonSymbols.ArrayBuffer)) {
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.arrayBufferInstance = resolver.resolveClass(<ClassPrototype>element, null);
}
if (element = this.lookupGlobal(CommonSymbols.String)) {
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.stringInstance = resolver.resolveClass(<ClassPrototype>element, null);
}
if (element = this.lookupGlobal(CommonSymbols.Array)) {
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.arrayPrototype = <ClassPrototype>element;
}
if (element = this.lookupGlobal(CommonSymbols.FixedArray)) {
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.fixedArrayPrototype = <ClassPrototype>element;
}
if (element = this.lookupGlobal(CommonSymbols.Set)) {
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.setPrototype = <ClassPrototype>element;
}
if (element = this.lookupGlobal(CommonSymbols.Map)) {
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.mapPrototype = <ClassPrototype>element;
}
if (element = this.lookupGlobal(CommonSymbols.abort)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.abortInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.runtime_allocate)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.allocateInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.memory_allocate)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.memoryAllocateInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.runtime_reallocate)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.reallocateInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.runtime_discard)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.discardInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.runtime_register)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.registerInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.runtime_makeArray)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.makeArrayInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.runtime_instanceof)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.instanceofInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
if (element = this.lookupGlobal(BuiltinSymbols.runtime_flags)) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.flagsInstance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
// memory allocator interface
if (element = this.lookupGlobal("__mem_allocate")) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.allocateMem = this.resolver.resolveFunction(<FunctionPrototype>element, null);
element = assert(this.lookupGlobal("__mem_free"));
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.freeMem = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
// garbage collector interface
if (this.lookupGlobal("__ref_collect")) {
if (element = this.lookupGlobal("__ref_link")) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.linkRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
if (element = this.lookupGlobal("__ref_unlink")) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.unlinkRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
}
element = assert(this.lookupGlobal("__ref_mark"));
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.markRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
this.collectorKind = CollectorKind.TRACING;
} else if (element = this.lookupGlobal("__ref_retain")) {
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.retainRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
element = assert(this.lookupGlobal("__ref_release"));
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.releaseRef = this.resolver.resolveFunction(<FunctionPrototype>element, null);
this.collectorKind = CollectorKind.COUNTING;
}
}
}
// register stdlib components
this.arrayBufferViewInstance = this.requireClass(CommonSymbols.ArrayBufferView);
this.arrayBufferInstance = this.requireClass(CommonSymbols.ArrayBuffer);
this.stringInstance = this.requireClass(CommonSymbols.String);
this.arrayPrototype = <ClassPrototype>this.require(CommonSymbols.Array, ElementKind.CLASS_PROTOTYPE);
this.fixedArrayPrototype = <ClassPrototype>this.require(CommonSymbols.FixedArray, ElementKind.CLASS_PROTOTYPE);
this.setPrototype = <ClassPrototype>this.require(CommonSymbols.Set, ElementKind.CLASS_PROTOTYPE);
this.mapPrototype = <ClassPrototype>this.require(CommonSymbols.Map, ElementKind.CLASS_PROTOTYPE);
this.abortInstance = this.requireFunction(CommonSymbols.abort);
this.allocInstance = this.requireFunction(CommonSymbols.alloc);
this.reallocInstance = this.requireFunction(CommonSymbols.realloc);
this.freeInstance = this.requireFunction(CommonSymbols.free);
this.retainInstance = this.requireFunction(CommonSymbols.retain);
this.releaseInstance = this.requireFunction(CommonSymbols.release);
this.retainReleaseInstance = this.requireFunction(CommonSymbols.retainRelease);
this.collectInstance = this.requireFunction(CommonSymbols.collect);
this.typeinfoInstance = this.requireFunction(CommonSymbols.typeinfo);
this.instanceofInstance = this.requireFunction(CommonSymbols.instanceof_);
this.visitInstance = this.requireFunction(CommonSymbols.visit);
this.allocArrayInstance = this.requireFunction(CommonSymbols.allocArray);
// mark module exports, i.e. to apply proper wrapping behavior on the boundaries
for (let file of this.filesByName.values()) {
@ -912,6 +825,30 @@ export class Program extends DiagnosticEmitter {
}
}
/** Requires that a global library element of the specified kind is present and returns it. */
private require(name: string, kind: ElementKind): Element {
var element = this.lookupGlobal(name);
if (!element) throw new Error("missing " + name);
if (element.kind != kind) throw new Error("unexpected " + name);
return element;
}
/** Requires that a non-generic global class is present and returns it. */
private requireClass(name: string): Class {
var prototype = this.require(name, ElementKind.CLASS_PROTOTYPE);
var resolved = this.resolver.resolveClass(<ClassPrototype>prototype, null);
if (!resolved) throw new Error("invalid " + name);
return resolved;
}
/** Requires that a non-generic global function is present and returns it. */
private requireFunction(name: string): Function {
var prototype = this.require(name, ElementKind.FUNCTION_PROTOTYPE);
var resolved = this.resolver.resolveFunction(<FunctionPrototype>prototype, null);
if (!resolved) throw new Error("invalid " + name);
return resolved;
}
/** Marks an element and its children as a module export. */
private markModuleExport(element: Element): void {
element.set(CommonFlags.MODULE_EXPORT);

View File

@ -153,11 +153,8 @@ export class Type {
/** Tests if this is a managed type that needs GC hooks. */
isManaged(program: Program): bool {
if (program.collectorKind != CollectorKind.NONE) {
let classReference = this.classReference;
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
}
return false;
var classReference = this.classReference;
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
}
/** Tests if this is a class type explicitly annotated as unmanaged. */