mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-07-27 20:22:10 +00:00
Initial GC integration (#196)
This commit is contained in:
lib
lint
loader
src
std/assembly
array.tsarraybuffer.tsbuiltins.ts
collector
diagnostics.tsgc.tsindex.d.tsinternal
map.tsmemory.tsset.tsstring.tstests/compiler
abi.optimized.watbinary.optimized.watbinary.untouched.watbuiltins.optimized.watcall-inferred.optimized.watcall-optional.optimized.watclass-extends.optimized.watclass-overloading.optimized.watclass-with-boolean-field.optimized.watclass.optimized.watcomma.optimized.watdeclare.optimized.watdo.optimized.watenum.optimized.watexport.optimized.watexports.optimized.watfor.optimized.watfunction-expression.optimized.watfunction-types.optimized.watfunction.optimized.watgetter-setter.optimized.wati64-polyfill.optimized.watif.optimized.watif.untouched.watimport.optimized.watinfer-type.optimized.watinlining-recursive.optimized.watinlining.optimized.watinstanceof.optimized.watlogical.optimized.watmain.optimized.watmandelbrot.optimized.watmany-locals.optimized.watmemcpy.optimized.watmemmove.optimized.watmemset.optimized.watnamed-export-default.optimized.watnamed-import-default.optimized.watnamespace.optimized.watnew-without-allocator.optimized.watobject-literal.optimized.watportable-conversions.optimized.watrecursive.optimized.watreexport.optimized.watretain-i32.optimized.watscoped.optimized.watstatic-this.optimized.wat
std
allocator_arena.optimized.watallocator_arena.untouched.watarray-access.optimized.watarray-access.untouched.watarray-literal.optimized.watarray-literal.tsarray-literal.untouched.watarray.optimized.watarray.untouched.watarraybuffer.optimized.watarraybuffer.untouched.watconstructor.optimized.watconstructor.untouched.watgc-basics.optimized.watgc-basics.tsgc-basics.untouched.watgc-integration.optimized.watgc-integration.tsgc-integration.untouched.watgc-object.optimized.watgc-object.tsgc-object.untouched.wathash.optimized.wathash.untouched.watlibm.optimized.watlibm.untouched.watmap.optimized.watmap.untouched.watmath.optimized.watmath.untouched.watmod.optimized.watnew.optimized.watoperator-overloading.optimized.watoperator-overloading.untouched.watpointer.optimized.watpolyfills.optimized.watset.optimized.watset.untouched.watstatic-array.optimized.watstatic-array.untouched.watstring-utf8.optimized.watstring-utf8.untouched.watstring.optimized.watstring.untouched.watsymbol.optimized.watsymbol.untouched.wattrace.optimized.wattypedarray.optimized.wattypedarray.untouched.wat
switch.optimized.watswitch.untouched.watternary.optimized.wattypealias.optimized.watunary.optimized.watvoid.optimized.watwhile.optimized.wat
193
src/builtins.ts
193
src/builtins.ts
@@ -134,7 +134,7 @@ export function compileCall(
|
||||
compiler.currentType = Type.bool;
|
||||
if (!type) return module.createUnreachable();
|
||||
let classType = type.classReference;
|
||||
return classType != null && classType.lookupOverload(OperatorKind.INDEXED_GET) != null
|
||||
return classType !== null && classType.lookupOverload(OperatorKind.INDEXED_GET) !== null
|
||||
? module.createI32(1)
|
||||
: module.createI32(0);
|
||||
}
|
||||
@@ -175,6 +175,19 @@ export function compileCall(
|
||||
compiler.currentType = Type.bool;
|
||||
return module.createI32(getExpressionId(expr) == ExpressionId.Const ? 1 : 0);
|
||||
}
|
||||
case "isManaged": { // isManaged<T>() -> bool
|
||||
if (!compiler.program.hasGC) {
|
||||
compiler.currentType = Type.bool;
|
||||
return module.createI32(0);
|
||||
}
|
||||
let type = evaluateConstantType(compiler, typeArguments, operands, reportNode);
|
||||
compiler.currentType = Type.bool;
|
||||
if (!type) return module.createUnreachable();
|
||||
let classType = type.classReference;
|
||||
return classType !== null && !classType.hasDecorator(DecoratorFlags.UNMANAGED)
|
||||
? module.createI32(1)
|
||||
: module.createI32(0);
|
||||
}
|
||||
|
||||
// math
|
||||
|
||||
@@ -2881,25 +2894,46 @@ export function compileAllocate(
|
||||
assert(classInstance.program == program);
|
||||
var module = compiler.module;
|
||||
var options = compiler.options;
|
||||
var allocateInstance = program.memoryAllocateInstance;
|
||||
if (!allocateInstance) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_find_name_0,
|
||||
reportNode.range, "memory.allocate"
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
|
||||
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
allocateInstance.internalName, [
|
||||
options.isWasm64
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset)
|
||||
],
|
||||
options.nativeSizeType
|
||||
);
|
||||
// __gc_allocate(size, markFn)
|
||||
if (program.hasGC && classInstance.type.isManaged(program)) {
|
||||
let allocateInstance = assert(program.gcAllocateInstance);
|
||||
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
allocateInstance.internalName, [
|
||||
options.isWasm64
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset),
|
||||
module.createI32(
|
||||
ensureGCHook(compiler, classInstance)
|
||||
)
|
||||
],
|
||||
options.nativeSizeType
|
||||
);
|
||||
|
||||
// memory.allocate(size)
|
||||
} else {
|
||||
let allocateInstance = program.memoryAllocateInstance;
|
||||
if (!allocateInstance) {
|
||||
program.error(
|
||||
DiagnosticCode.Cannot_find_name_0,
|
||||
reportNode.range, "memory.allocate"
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
|
||||
|
||||
compiler.currentType = classInstance.type;
|
||||
return module.createCall(
|
||||
allocateInstance.internalName, [
|
||||
options.isWasm64
|
||||
? module.createI64(classInstance.currentMemoryOffset)
|
||||
: module.createI32(classInstance.currentMemoryOffset)
|
||||
],
|
||||
options.nativeSizeType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Compiles an abort wired to the conditionally imported 'abort' function. */
|
||||
@@ -2921,7 +2955,7 @@ export function compileAbort(
|
||||
? compiler.compileExpression(message, stringType, ConversionKind.IMPLICIT, WrapMode.NONE)
|
||||
: stringType.toNativeZero(module);
|
||||
|
||||
var filenameArg = compiler.compileStaticString(reportNode.range.source.normalizedPath);
|
||||
var filenameArg = compiler.ensureStaticString(reportNode.range.source.normalizedPath);
|
||||
|
||||
compiler.currentType = Type.void;
|
||||
return module.createBlock(null, [
|
||||
@@ -2988,3 +3022,122 @@ export function compileIterateRoots(compiler: Compiler): void {
|
||||
: module.createNop()
|
||||
);
|
||||
}
|
||||
|
||||
/** Ensures that the specified class's GC hook exists and returns its function table index. */
|
||||
export function ensureGCHook(
|
||||
compiler: Compiler,
|
||||
classInstance: Class
|
||||
): u32 {
|
||||
var program = compiler.program;
|
||||
assert(classInstance.type.isManaged(program));
|
||||
|
||||
// check if the GC hook has already been created
|
||||
{
|
||||
let existingIndex = classInstance.gcHookIndex;
|
||||
if (existingIndex != <u32>-1) return existingIndex;
|
||||
}
|
||||
|
||||
// check if the class implements a custom GC function (only valid for internals)
|
||||
var members = classInstance.members;
|
||||
if (classInstance.prototype.declaration.range.source.isLibrary) {
|
||||
if (members !== null && members.has("__gc")) {
|
||||
let gcPrototype = assert(members.get("__gc"));
|
||||
assert(gcPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
|
||||
let gcInstance = assert(program.resolver.resolveFunction(<FunctionPrototype>gcPrototype, null));
|
||||
assert(gcInstance.is(CommonFlags.PRIVATE | CommonFlags.INSTANCE));
|
||||
assert(!gcInstance.isAny(CommonFlags.AMBIENT | CommonFlags.VIRTUAL));
|
||||
assert(gcInstance.signature.parameterTypes.length == 0);
|
||||
assert(gcInstance.signature.returnType == Type.void);
|
||||
gcInstance.internalName = classInstance.internalName + "~gc";
|
||||
assert(compiler.compileFunction(gcInstance));
|
||||
let index = compiler.ensureFunctionTableEntry(gcInstance);
|
||||
classInstance.gcHookIndex = index;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
var module = compiler.module;
|
||||
var options = compiler.options;
|
||||
var nativeSizeType = options.nativeSizeType;
|
||||
var nativeSizeSize = options.usizeType.byteSize;
|
||||
var body = new Array<ExpressionRef>();
|
||||
|
||||
// nothing to mark if 'this' is null
|
||||
body.push(
|
||||
module.createIf(
|
||||
module.createUnary(
|
||||
options.isWasm64
|
||||
? UnaryOp.EqzI64
|
||||
: UnaryOp.EqzI32,
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
),
|
||||
module.createReturn()
|
||||
)
|
||||
);
|
||||
|
||||
// remember the function index so we don't recurse infinitely
|
||||
var functionTable = compiler.functionTable;
|
||||
var gcHookIndex = functionTable.length;
|
||||
functionTable.push(0);
|
||||
classInstance.gcHookIndex = gcHookIndex;
|
||||
|
||||
// if the class extends a base class, call its hook first (calls mark)
|
||||
var baseInstance = classInstance.base;
|
||||
if (baseInstance) {
|
||||
assert(baseInstance.type.isManaged(program));
|
||||
body.push(
|
||||
module.createCallIndirect(
|
||||
module.createI32(
|
||||
ensureGCHook(compiler, <Class>baseInstance.type.classReference)
|
||||
),
|
||||
[
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
],
|
||||
nativeSizeType == NativeType.I64 ? "Iv" : "iv"
|
||||
)
|
||||
);
|
||||
|
||||
// if this class is the top-most base class, mark the instance
|
||||
} else {
|
||||
body.push(
|
||||
module.createCall(assert(program.gcMarkInstance).internalName, [
|
||||
module.createGetLocal(0, nativeSizeType)
|
||||
], NativeType.None)
|
||||
);
|
||||
}
|
||||
|
||||
// mark instances assigned to own fields that are again references
|
||||
if (members) {
|
||||
for (let member of members.values()) {
|
||||
if (member.kind == ElementKind.FIELD) {
|
||||
if ((<Field>member).parent === classInstance) {
|
||||
let type = (<Field>member).type;
|
||||
if (type.isManaged(program)) {
|
||||
let offset = (<Field>member).memoryOffset;
|
||||
assert(offset >= 0);
|
||||
body.push(
|
||||
module.createCall(assert(program.gcMarkInstance).internalName, [
|
||||
module.createLoad(
|
||||
nativeSizeSize,
|
||||
false,
|
||||
module.createGetLocal(0, nativeSizeType),
|
||||
nativeSizeType,
|
||||
offset
|
||||
)
|
||||
], NativeType.None)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the function to the module and return its table index
|
||||
functionTable[gcHookIndex] = module.addFunction(
|
||||
classInstance.internalName + "~gc",
|
||||
compiler.ensureFunctionType(null, Type.void, options.usizeType),
|
||||
null,
|
||||
module.createBlock(null, body)
|
||||
);
|
||||
return gcHookIndex;
|
||||
}
|
||||
|
Reference in New Issue
Block a user