mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-21 18:51:43 +00:00
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:
@ -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;
|
||||
|
805
src/compiler.ts
805
src/compiler.ts
File diff suppressed because it is too large
Load Diff
65
src/flow.ts
65
src/flow.ts
@ -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. */
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user