rt hooks all over the place

still some work to do on optimizing away retain/release calls, but this looks promising
This commit is contained in:
dcode
2019-05-15 21:17:41 +02:00
parent cb09edf677
commit f73d807d5a
99 changed files with 8237 additions and 18460 deletions

View File

@ -465,8 +465,8 @@ export namespace BuiltinSymbols {
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";
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";
@ -652,7 +652,7 @@ export function compileCall(
let type = evaluateConstantType(compiler, typeArguments, operands, reportNode);
compiler.currentType = Type.bool;
if (!type) return module.createUnreachable();
return module.createI32(type.isManaged(compiler.program) ? 1 : 0);
return module.createI32(type.isManaged ? 1 : 0);
}
case BuiltinSymbols.sizeof: { // sizeof<T!>() -> usize
compiler.currentType = compiler.options.usizeType;
@ -4116,12 +4116,12 @@ export function compileVisitMembers(compiler: Compiler): void {
var ftype = compiler.ensureFunctionType([ usizeType, Type.i32 ], Type.void); // ref, cookie
var managedClasses = program.managedClasses;
var visitInstance = assert(program.visitInstance);
var names: string[] = [ "invalid" ]; // classId=0 is invalid
var names: string[] = [ "invalid" ]; // classId=0 is invalid (TODO: make this ArrayBuffer?)
var blocks = new Array<ExpressionRef[]>();
var lastId = 0;
for (let [id, instance] of managedClasses) {
assert(instance.type.isManaged(program));
assert(instance.type.isManaged);
assert(id == ++lastId);
names.push(instance.internalName);
@ -4139,7 +4139,8 @@ export function compileVisitMembers(compiler: Compiler): void {
}
blocks.push([
module.createCall(traverseFunc.internalName, [
module.createGetLocal(0, nativeSizeType)
module.createGetLocal(0, nativeSizeType), // ref
module.createGetLocal(1, NativeType.I32) // cookie
], NativeType.None),
module.createReturn()
]);
@ -4154,9 +4155,7 @@ export function compileVisitMembers(compiler: Compiler): void {
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.id;
if (fieldType.isManaged) {
let fieldOffset = (<Field>member).memoryOffset;
assert(fieldOffset >= 0);
block.push(
@ -4170,11 +4169,12 @@ export function compileVisitMembers(compiler: Compiler): void {
),
module.createBlock(null, [
module.createCall(visitInstance.internalName, [
module.createGetLocal(2, nativeSizeType)
module.createGetLocal(2, nativeSizeType), // ref
module.createGetLocal(1, NativeType.I32) // cookie
], NativeType.None),
module.createCall(BuiltinSymbols.visit_members, [
module.createI32(fieldClassId),
module.createGetLocal(2, nativeSizeType)
module.createGetLocal(2, nativeSizeType), // ref
module.createGetLocal(1, NativeType.I32) // cookie
], NativeType.None)
])
)
@ -4226,10 +4226,10 @@ export function compileVisitMembers(compiler: Compiler): void {
module.addFunction(BuiltinSymbols.visit_members, ftype, [ nativeSizeType ], current);
}
function typeToRuntimeFlags(type: Type, program: Program): RTTIFlags {
function typeToRuntimeFlags(type: Type): RTTIFlags {
var flags = RTTIFlags.VALUE_ALIGN_0 * (1 << type.alignLog2);
if (type.is(TypeFlags.NULLABLE)) flags |= RTTIFlags.VALUE_NULLABLE;
if (type.isManaged(program)) flags |= RTTIFlags.VALUE_MANAGED;
if (type.isManaged) flags |= RTTIFlags.VALUE_MANAGED;
return flags / RTTIFlags.VALUE_ALIGN_0;
}
@ -4243,9 +4243,9 @@ export function compileRTTI(compiler: Compiler): void {
var data = new Uint8Array(size);
writeI32(count, data, 0);
var off = 8;
var arrayPrototype = assert(program.arrayPrototype);
var setPrototype = assert(program.setPrototype);
var mapPrototype = assert(program.mapPrototype);
var arrayPrototype = program.arrayPrototype;
var setPrototype = program.setPrototype;
var mapPrototype = program.mapPrototype;
var lastId = 0;
for (let [id, instance] of managedClasses) {
assert(id == ++lastId);
@ -4255,18 +4255,18 @@ export function compileRTTI(compiler: Compiler): void {
let typeArguments = assert(instance.getTypeArgumentsTo(arrayPrototype));
assert(typeArguments.length == 1);
flags |= RTTIFlags.ARRAY;
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0], program);
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
} else if (instance.prototype.extends(setPrototype)) {
let typeArguments = assert(instance.getTypeArgumentsTo(setPrototype));
assert(typeArguments.length == 1);
flags |= RTTIFlags.SET;
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0], program);
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
} else if (instance.prototype.extends(mapPrototype)) {
let typeArguments = assert(instance.getTypeArgumentsTo(mapPrototype));
assert(typeArguments.length == 2);
flags |= RTTIFlags.MAP;
flags |= RTTIFlags.KEY_ALIGN_0 * typeToRuntimeFlags(typeArguments[0], program);
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[1], program);
flags |= RTTIFlags.KEY_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[1]);
}
writeI32(flags, data, off); off += 4;
let base = instance.base;

File diff suppressed because it is too large Load Diff

View File

@ -146,16 +146,32 @@ export enum LocalFlags {
READFROM = 1 << 2,
/** Local is written to. */
WRITTENTO = 1 << 3,
/** Local must be autoreleased. */
AUTORELEASE = 1 << 4,
/** Local is conditionally read from. */
CONDITIONALLY_READFROM = 1 << 4,
CONDITIONALLY_READFROM = 1 << 5,
/** Local is conditionally written to. */
CONDITIONALLY_WRITTENTO = 1 << 5,
CONDITIONALLY_WRITTENTO = 1 << 6,
/** Local must be conditionally autoreleased. */
CONDITIONALLY_AUTORELEASE = 1 << 7,
/** Any categorical flag. */
ANY_CATEGORICAL = WRAPPED | NONNULL | READFROM | WRITTENTO,
ANY_CATEGORICAL = WRAPPED
| NONNULL
| READFROM
| WRITTENTO
| AUTORELEASE,
/** Any conditional flag. */
ANY_CONDITIONAL = CONDITIONALLY_READFROM | CONDITIONALLY_WRITTENTO
ANY_CONDITIONAL = AUTORELEASE
| CONDITIONALLY_READFROM
| CONDITIONALLY_WRITTENTO
| CONDITIONALLY_AUTORELEASE,
/** Any autorelease flag. */
ANY_AUTORELEASE = AUTORELEASE
| CONDITIONALLY_AUTORELEASE
}
export namespace LocalFlags {
export function join(left: LocalFlags, right: LocalFlags): LocalFlags {
@ -301,6 +317,17 @@ export class Flow {
return local;
}
/** Gets a local that sticks around until this flow is exited, and then released. */
getAutoreleaseLocal(type: Type): Local {
var local = this.getTempLocal(type);
local.set(CommonFlags.SCOPED);
var scopedLocals = this.scopedLocals;
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
scopedLocals.set("~auto" + (this.parentFunction.nextAutoreleaseId++), local);
this.setLocalFlag(local.index, LocalFlags.AUTORELEASE);
return local;
}
/** Frees the temporary local for reuse. */
freeTempLocal(local: Local): void {
if (local.is(CommonFlags.INLINED)) return;
@ -478,14 +505,21 @@ export class Flow {
return this.actualFunction.lookup(name);
}
/** Tests if the local at the specified index has the specified flag. */
/** Tests if the local at the specified index has the specified flag or flags. */
isLocalFlag(index: i32, flag: LocalFlags, defaultIfInlined: bool = true): bool {
if (index < 0) return defaultIfInlined;
var localFlags = this.localFlags;
return index < localFlags.length && (unchecked(this.localFlags[index]) & flag) == flag;
}
/** Tests if the local at the specified index has any of the specified flags. */
isAnyLocalFlag(index: i32, flag: LocalFlags, defaultIfInlined: bool = true): bool {
if (index < 0) return defaultIfInlined;
var localFlags = this.localFlags;
return index < localFlags.length && (unchecked(this.localFlags[index]) & flag) != 0;
}
/** Sets the specified flag on the local at the specified index. */
/** Sets the specified flag or flags on the local at the specified index. */
setLocalFlag(index: i32, flag: LocalFlags): void {
if (index < 0) return;
var localFlags = this.localFlags;
@ -493,7 +527,7 @@ export class Flow {
this.localFlags[index] = flags | flag;
}
/** Unsets the specified flag on the local at the specified index. */
/** Unsets the specified flag or flags on the local at the specified index. */
unsetLocalFlag(index: i32, flag: LocalFlags): void {
if (index < 0) return;
var localFlags = this.localFlags;
@ -548,6 +582,13 @@ export class Flow {
if (other.is(FlowFlags.ALLOCATES)) {
this.set(FlowFlags.CONDITIONALLY_ALLOCATES);
}
var localFlags = other.localFlags;
for (let i = 0, k = localFlags.length; i < k; ++i) {
let flags = localFlags[i];
if (flags & LocalFlags.AUTORELEASE) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_AUTORELEASE);
if (flags & LocalFlags.READFROM) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_READFROM);
if (flags & LocalFlags.WRITTENTO) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_WRITTENTO);
}
}
/** Inherits mutual flags and local wrap states from the specified flows (e.g. then with else). */
@ -1023,6 +1064,16 @@ export class Flow {
}
return true;
}
toString(): string {
var levels = 0;
var parent = this.parent;
while (parent) {
parent = parent.parent;
++levels;
}
return "Flow(" + this.actualFunction + ")[" + levels.toString() + "]";
}
}
/** Tests if a conversion from one type to another can technically overflow. */

View File

@ -410,17 +410,26 @@ export class Program extends DiagnosticEmitter {
this.resolver = new Resolver(this);
}
/** Writes a common runtime header to the specified buffer. */
writeRuntimeHeader(buffer: Uint8Array, offset: i32, classInstance: Class, payloadSize: u32): void {
// BLOCK {
// mmInfo: usize // WASM64 TODO
// gcInfo: u32
// rtId: u32
// rtSize: u32
// }
assert(payloadSize < (1 << 28)); // 1 bit BUFFERED + 3 bits color
writeI32(payloadSize, buffer, offset);
writeI32(1, buffer, offset + 4); // RC=1
writeI32(classInstance.id, buffer, offset + 8);
writeI32(payloadSize, buffer, offset + 12);
}
/** Gets the size of a runtime header. */
get runtimeHeaderSize(): i32 {
return 16;
}
/** Writes a common runtime header to the specified buffer. */
writeRuntimeHeader(buffer: Uint8Array, offset: i32, classId: i32, payloadSize: u32): void {
writeI32(classId, buffer, offset);
writeI32(payloadSize, buffer, offset + 4);
}
/** Creates a native variable declaration. */
makeNativeVariableDeclaration(
/** The simple name of the variable */
@ -1898,6 +1907,8 @@ export abstract class Element {
isAny(flags: CommonFlags): bool { return (this.flags & flags) != 0; }
/** Sets a specific flag or flags. */
set(flag: CommonFlags): void { this.flags |= flag; }
/** Unsets the specific flag or flags. */
unset(flag: CommonFlags): void {this.flags &= ~flag; }
/** Tests if this element has a specific decorator flag or flags. */
hasDecorator(flag: DecoratorFlags): bool { return (this.decoratorFlags & flag) == flag; }
@ -2544,6 +2555,8 @@ export class Function extends TypedElement {
nextInlineId: i32 = 0;
/** Counting id of anonymous inner functions. */
nextAnonymousId: i32 = 0;
/** Counting id of autorelease variables. */
nextAutoreleaseId: i32 = 0;
/** Constructs a new concrete function. */
constructor(

View File

@ -152,7 +152,7 @@ export class Type {
}
/** Tests if this is a managed type that needs GC hooks. */
isManaged(program: Program): bool {
get isManaged(): bool {
var classReference = this.classReference;
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
}