directize more (eliminate table use)

This commit is contained in:
dcode
2019-04-02 16:18:44 +02:00
parent 6bf8276746
commit e1bd0050e2
45 changed files with 4202 additions and 5869 deletions

View File

@ -3650,9 +3650,7 @@ export function compileCall(
);
return module.createUnreachable();
}
let id = classReference.ensureId(compiler); // involves compile steps
compiler.currentType = Type.u32;
return module.createI32(id);
return module.createI32(classReference.ensureId());
}
case BuiltinSymbols.gc_mark_roots: {
if (
@ -3666,6 +3664,20 @@ export function compileCall(
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)
) {
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.needsTraverse = true;
compiler.currentType = Type.void;
return module.createCall(BuiltinSymbols.gc_mark_members, [ arg0, arg1 ], NativeType.None);
}
}
// try to defer inline asm to a concrete built-in
@ -4101,20 +4113,112 @@ export function compileMarkRoots(compiler: Compiler): void {
);
}
// TODO
export function compileMarkMembers(compiler: Compiler): void {
var program = compiler.program;
var module = compiler.module;
var ftype = compiler.ensureFunctionType(null, Type.void);
var usizeType = program.options.usizeType;
var nativeSizeType = usizeType.toNativeType();
var nativeSizeSize = usizeType.byteSize;
var ftype = compiler.ensureFunctionType([ Type.i32, usizeType ], Type.void);
var managedClasses = program.managedClasses;
var markRef = assert(program.markRef);
var names: string[] = [ "invalid" ]; // classId=0 is invalid
var blocks = new Array<ExpressionRef[]>();
var lastId = 0;
var names = new Array<string>();
var current = module.createSwitch(names, "invalid", module.createGetLocal(0, NativeType.I32));
for (let [id, instance] of managedClasses) {
assert(instance.type.isManaged(program));
assert(id == ++lastId);
names.push(instance.internalName);
module.addFunction(BuiltinSymbols.gc_mark_members, ftype, [], module.createBlock(null, [
module.createBlock("invalid", [
current
]),
module.createUnreachable()
]));
let traverseImpl = instance.lookupInSelf("__traverse");
// if a library element, check if it implements a custom traversal function
if (instance.isDeclaredInLibrary && traverseImpl) {
assert(traverseImpl.kind == ElementKind.FUNCTION_PROTOTYPE);
let traverseFunc = program.resolver.resolveFunction(<FunctionPrototype>traverseImpl, null);
if (!traverseFunc || !compiler.compileFunction(traverseFunc)) {
blocks.push([
module.createUnreachable()
]);
continue;
}
blocks.push([
module.createCall(traverseFunc.internalName, [
module.createGetLocal(1, nativeSizeType)
], NativeType.None),
module.createReturn()
]);
// otherwise generate one
} else {
// traverse references assigned to own fields
let block = new Array<ExpressionRef>();
let members = instance.members;
if (members) {
for (let member of members.values()) {
if (member.kind == ElementKind.FIELD) {
if ((<Field>member).parent === instance) {
let fieldType = (<Field>member).type;
if (fieldType.isManaged(program)) {
let fieldClass = fieldType.classReference!;
let fieldClassId = fieldClass.ensureId();
let fieldOffset = (<Field>member).memoryOffset;
assert(fieldOffset >= 0);
block.push(
// if ($2 = value) FIELDCLASS~traverse($2)
module.createIf(
module.createTeeLocal(2,
module.createLoad(
nativeSizeSize,
false,
module.createGetLocal(1, nativeSizeType),
nativeSizeType,
fieldOffset
)
),
module.createBlock(null, [
module.createCall(markRef.internalName, [
module.createGetLocal(2, nativeSizeType)
], NativeType.None),
module.createCall(BuiltinSymbols.gc_mark_members, [
module.createI32(fieldClassId),
module.createGetLocal(2, nativeSizeType)
], NativeType.None)
])
)
);
}
}
}
}
}
block.push(module.createReturn());
blocks.push(block);
}
}
var current: ExpressionRef;
if (blocks.length) {
// create a big switch mapping class ids to traversal logic
current = module.createBlock(names[1], [
module.createSwitch(names, "invalid", module.createGetLocal(0, NativeType.I32))
]);
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);
// wrap the function with a terminating unreachable
current = module.createBlock(null, [
current,
module.createUnreachable()
]);
} else {
// simplify
current = module.createUnreachable();
}
module.addFunction(BuiltinSymbols.gc_mark_members, ftype, [ nativeSizeType ], current);
}
// Helpers

View File

@ -7,6 +7,7 @@ import {
compileCall as compileBuiltinCall,
compileAbort,
compileMarkRoots,
compileMarkMembers,
BuiltinSymbols
} from "./builtins";
@ -392,6 +393,12 @@ export class Compiler extends DiagnosticEmitter {
if (!explicitStartFunction) module.setStart(funcRef);
}
// compile gc integration if necessary
if (this.needsTraverse) {
compileMarkRoots(this);
compileMarkMembers(this);
}
// update the heap base pointer
var memoryOffset = this.memoryOffset;
memoryOffset = i64_align(memoryOffset, options.usizeType.byteSize);
@ -443,12 +450,6 @@ export class Compiler extends DiagnosticEmitter {
if (file.source.isEntry) this.ensureModuleExports(file);
}
// set up gc
if (this.needsTraverse) {
compileMarkRoots(this);
// compileMarkMembers(this);
}
// expose module capabilities
var capabilities = Capability.NONE;
if (program.options.isWasm64) capabilities |= Capability.WASM64;
@ -1412,7 +1413,7 @@ export class Compiler extends DiagnosticEmitter {
} else {
let length = stringValue.length;
let buffer = new Uint8Array(rtHeaderSize + (length << 1));
program.writeRuntimeHeader(buffer, 0, stringInstance.ensureId(this), length << 1);
program.writeRuntimeHeader(buffer, 0, stringInstance.ensureId(), length << 1);
for (let i = 0; i < length; ++i) {
writeI16(stringValue.charCodeAt(i), buffer, rtHeaderSize + (i << 1));
}
@ -1438,7 +1439,7 @@ export class Compiler extends DiagnosticEmitter {
var runtimeHeaderSize = program.runtimeHeaderSize;
var buf = new Uint8Array(runtimeHeaderSize + byteLength);
program.writeRuntimeHeader(buf, 0, bufferInstance.ensureId(this), byteLength);
program.writeRuntimeHeader(buf, 0, bufferInstance.ensureId(), byteLength);
var pos = runtimeHeaderSize;
var nativeType = elementType.toNativeType();
switch (nativeType) {
@ -1525,7 +1526,7 @@ export class Compiler extends DiagnosticEmitter {
var arrayLength = i32(bufferLength / elementType.byteSize);
var buf = new Uint8Array(runtimeHeaderSize + arrayInstanceSize);
program.writeRuntimeHeader(buf, 0, arrayInstance.ensureId(this), arrayInstanceSize);
program.writeRuntimeHeader(buf, 0, arrayInstance.ensureId(), arrayInstanceSize);
var bufferAddress32 = i64_low(bufferSegment.offset) + runtimeHeaderSize;
assert(!program.options.isWasm64); // TODO
@ -6828,7 +6829,7 @@ export class Compiler extends DiagnosticEmitter {
// makeArray(length, classId, alignLog2, staticBuffer)
let expr = this.makeCallDirect(assert(program.makeArrayInstance), [
module.createI32(length),
module.createI32(arrayInstance.ensureId(this)),
module.createI32(arrayInstance.ensureId()),
program.options.isWasm64
? module.createI64(elementType.alignLog2)
: module.createI32(elementType.alignLog2),
@ -6862,7 +6863,7 @@ export class Compiler extends DiagnosticEmitter {
module.createSetLocal(tempThis.index,
this.makeCallDirect(makeArrayInstance, [
module.createI32(length),
module.createI32(arrayInstance.ensureId(this)),
module.createI32(arrayInstance.ensureId()),
program.options.isWasm64
? module.createI64(elementType.alignLog2)
: module.createI32(elementType.alignLog2),
@ -8123,7 +8124,7 @@ export class Compiler extends DiagnosticEmitter {
? module.createI64(classInstance.currentMemoryOffset)
: module.createI32(classInstance.currentMemoryOffset)
], reportNode),
module.createI32(classInstance.ensureId(this))
module.createI32(classInstance.ensureId())
], reportNode);
}
}
@ -8328,7 +8329,7 @@ export class Compiler extends DiagnosticEmitter {
module.createBreak(label,
module.createBinary(BinaryOp.EqI32, // classId == class.id
module.createTeeLocal(idTemp.index, idExpr),
module.createI32(classInstance.ensureId(this))
module.createI32(classInstance.ensureId())
),
module.createI32(1) // ? true
)
@ -8408,7 +8409,7 @@ export class Compiler extends DiagnosticEmitter {
var baseInstance = classInstance.base;
if (baseInstance) {
let baseType = baseInstance.type;
let baseClassId = baseInstance.ensureId(this);
let baseClassId = baseInstance.ensureId();
assert(baseType.isManaged(program));
body.push(
// BASECLASS~traverse.call(this)
@ -8429,7 +8430,7 @@ export class Compiler extends DiagnosticEmitter {
let fieldType = (<Field>member).type;
if (fieldType.isManaged(program)) {
let fieldClass = fieldType.classReference!;
let fieldClassId = fieldClass.ensureId(this);
let fieldClassId = fieldClass.ensureId();
let fieldOffset = (<Field>member).memoryOffset;
assert(fieldOffset >= 0);
hasRefFields = true;

View File

@ -346,6 +346,8 @@ export class Program extends DiagnosticEmitter {
instancesByName: Map<string,Element> = new Map();
/** Classes backing basic types like `i32`. */
typeClasses: Map<TypeKind,Class> = new Map();
/** Managed classes contained in the program, by id. */
managedClasses: Map<i32,Class> = new Map();
// runtime references
@ -3017,22 +3019,13 @@ export class Class extends TypedElement {
private _id: u32 = 0;
/** Ensures that this class has an id. */
ensureId(compiler: Compiler): i32 {
ensureId(): i32 {
var id = this._id;
if (!id) {
assert(!this.hasDecorator(DecoratorFlags.UNMANAGED));
let program = this.program;
if (program.collectorKind == CollectorKind.TRACING) {
// tracing GC uses the function index of the iteration function as the
// class's id so it can call the id directly, which avoids to generate
// a helper function with a big switch mapping ids to function indexes.
// here: might be called recursively in makeIterate, so reserve the id.
this._id = id = compiler.makeTraverseReserve(this);
compiler.makeTraverse(this, id);
} else {
// counting GC or none just increments without any iterate functions
this._id = id = program.nextClassId++;
}
this._id = id = program.nextClassId++;
program.managedClasses.set(id, this);
}
return id;
}