mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-20 18:26:40 +00:00
Initial new rt integration
This commit is contained in:
116
src/builtins.ts
116
src/builtins.ts
@ -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 {
|
||||
|
@ -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
|
||||
|
732
src/compiler.ts
732
src/compiler.ts
@ -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,
|
||||
|
215
src/program.ts
215
src/program.ts
@ -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);
|
||||
|
@ -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. */
|
||||
|
Reference in New Issue
Block a user