Skip to content

Commit

Permalink
WIP has optimize bug
Browse files Browse the repository at this point in the history
  • Loading branch information
HerrCai0907 committed Oct 7, 2023
1 parent e3e4166 commit 6b35f1e
Show file tree
Hide file tree
Showing 13 changed files with 4,456 additions and 40 deletions.
25 changes: 24 additions & 1 deletion src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ import {
NodeKind,
LiteralExpression,
ArrayLiteralExpression,
IdentifierExpression
IdentifierExpression,
FunctionExpression
} from "./ast";

import {
Expand Down Expand Up @@ -194,6 +195,8 @@ export namespace BuiltinNames {
export const instantiate = "~lib/builtins/instantiate";
export const idof = "~lib/builtins/idof";

export const experimental_first_class_function = "~lib/builtins/experimental_first_class_function";

export const i8 = "~lib/builtins/i8";
export const i16 = "~lib/builtins/i16";
export const i32 = "~lib/builtins/i32";
Expand Down Expand Up @@ -3609,6 +3612,26 @@ function builtin_unchecked(ctx: BuiltinFunctionContext): ExpressionRef {
}
builtinFunctions.set(BuiltinNames.unchecked, builtin_unchecked);

// experimental_first_class_function(FunctionExpression: *) -> *
function builtin_experimental_first_class_function(ctx: BuiltinFunctionContext): ExpressionRef {
let compiler = ctx.compiler;
let module = compiler.module;
if (
checkTypeAbsent(ctx) |
checkArgsRequired(ctx, 1)
) return module.unreachable();
let operand = ctx.operands[0];
if (operand.kind != NodeKind.Function) {
let prototype = ctx.prototype;
prototype.program.error(DiagnosticCode._0_expected, operand.range, "FunctionExpression");
return module.unreachable();
}
let functionExpression = <FunctionExpression>operand;
let expr = compiler.compileFirstClassFunction(functionExpression);
return expr;
}
builtinFunctions.set(BuiltinNames.experimental_first_class_function, builtin_experimental_first_class_function);

// call_indirect<T?>(index: u32, ...args: *[]) -> T
function builtin_call_indirect(ctx: BuiltinFunctionContext): ExpressionRef {
let compiler = ctx.compiler;
Expand Down
1 change: 1 addition & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export namespace CommonNames {
export const valueof = "valueof";
export const returnof = "returnof";
export const nonnull = "nonnull";
export const experimental_first_class_function = "experimental_first_class_function";
// aliases
export const null_ = "null";
export const true_ = "true";
Expand Down
86 changes: 75 additions & 11 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ import {
TypeKind,
TypeFlags,
Signature,
typesToRefs
typesToRefs,
SignatureFlags
} from "./types";

import {
Expand Down Expand Up @@ -2054,20 +2055,23 @@ export class Compiler extends DiagnosticEmitter {

// === Table ====================================================================================

private registerFunctionInTable(instance: Function): u32 {
// Add to the function table
let functionTable = this.functionTable;
let tableBase = this.options.tableBase;
if (!tableBase) tableBase = 1; // leave first elem blank
let index = tableBase + functionTable.length;
functionTable.push(instance);
return index;
}

/** Ensures that a runtime counterpart of the specified function exists and returns its address. */
ensureRuntimeFunction(instance: Function): i64 {
assert(instance.is(CommonFlags.Compiled) && !instance.is(CommonFlags.Stub));
let program = this.program;
let memorySegment = instance.memorySegment;
if (!memorySegment) {

// Add to the function table
let functionTable = this.functionTable;
let tableBase = this.options.tableBase;
if (!tableBase) tableBase = 1; // leave first elem blank
let index = tableBase + functionTable.length;
functionTable.push(instance);

let index = this.registerFunctionInTable(instance);
// Create runtime function
let rtInstance = assert(this.resolver.resolveClass(program.functionPrototype, [ instance.type ]));
let buf = rtInstance.createBuffer();
Expand Down Expand Up @@ -6082,12 +6086,13 @@ export class Compiler extends DiagnosticEmitter {
let functionArg = this.compileExpression(expression.expression, Type.auto);
let signature = this.currentType.getSignature();
if (signature) {
const thisArg = signature.hasEnv ? functionArg : 0;
return this.compileCallIndirect(
signature,
functionArg,
expression.args,
expression,
0,
thisArg,
contextualType == Type.void
);
}
Expand Down Expand Up @@ -7035,6 +7040,65 @@ export class Compiler extends DiagnosticEmitter {
return module.unreachable();
}

compileFirstClassFunction(
expression: FunctionExpression
// TODO(support contextualType)
): ExpressionRef {
// class AnonymousFunctionClass extends Function<T> {}
let module = this.module;
let flow = this.currentFlow;
let sourceFunction = flow.sourceFunction;
let declaration = expression.declaration.clone();
let anonymousId = sourceFunction.nextAnonymousId++;
let contextualTypeArguments = cloneMap(flow.contextualTypeArguments);

let classPrototype = new ClassPrototype(
`${sourceFunction.internalName}|anonymous|${anonymousId}`,
sourceFunction,
Node.createClassDeclaration(
Node.createIdentifierExpression(
`${sourceFunction.internalName}|anonymous|${anonymousId}`, declaration.range
),
null,
CommonFlags.None,
null,
null,
null,
[],
declaration.range
),
DecoratorFlags.None
);
let classInstance = this.resolver.resolveClass(classPrototype, null, contextualTypeArguments, ReportMode.Report);
if (!classInstance) return this.module.unreachable();

declaration.flags |= CommonFlags.Instance;
let functionPrototype = new FunctionPrototype(
`anonymous|${anonymousId}`,
classInstance,
declaration,
DecoratorFlags.None
);

let instance = this.resolver.resolveFunction(functionPrototype, null, contextualTypeArguments);
if (!instance) return this.module.unreachable();
instance.signature.setEnv();
instance.flow.outer = flow;

let worked = this.compileFunction(instance);
this.currentType = instance.signature.type;
if (!worked) return module.unreachable();

let currentType = this.currentType;
if (!instance) return module.unreachable();
let rtInstance = assert(this.resolver.resolveClass(this.program.functionPrototype, [ instance.type ]));
const functionIndexInTable = this.registerFunctionInTable(instance);
let ctor = this.ensureConstructor(rtInstance, expression);
let expr = this.makeCallDirect(ctor, [module.i32(0), module.i32(functionIndexInTable), module.usize(0)], expression, /* immediatelyDropped */ false);
this.currentType = currentType;
return expr;
}

private compileFunctionExpression(
expression: FunctionExpression,
contextualType: Type,
Expand Down Expand Up @@ -8771,7 +8835,7 @@ export class Compiler extends DiagnosticEmitter {
classInstance.type,
classInstance.type,
baseCtor.signature.requiredParameters,
baseCtor.signature.hasRest
baseCtor.signature.hasRest ? SignatureFlags.Rest : SignatureFlags.None
),
contextualTypeArguments
);
Expand Down
6 changes: 6 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,12 @@ export class Program extends DiagnosticEmitter {
this.makeNativeTypeDeclaration(CommonNames.nonnull, CommonFlags.Export | CommonFlags.Generic),
DecoratorFlags.Builtin
));
this.nativeFile.add(CommonNames.experimental_first_class_function, new TypeDefinition(
CommonNames.experimental_first_class_function,
this.nativeFile,
this.makeNativeTypeDeclaration(CommonNames.experimental_first_class_function, CommonFlags.Export | CommonFlags.Generic),
DecoratorFlags.Builtin
));

// The following types might not be enabled by compiler options, so the
// compiler needs to check this condition whenever such a value is created
Expand Down
36 changes: 31 additions & 5 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ import {
Signature,
typesToString,
TypeKind,
TypeFlags
TypeFlags,
SignatureFlags,
} from "./types";

import {
Expand Down Expand Up @@ -309,6 +310,9 @@ export class Resolver extends DiagnosticEmitter {
if (text == CommonNames.valueof) return this.resolveBuiltinValueofType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.returnof) return this.resolveBuiltinReturnTypeType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.nonnull) return this.resolveBuiltinNotNullableType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.experimental_first_class_function) {
return this.resolveBuiltinFirstClassFunctionType(node, ctxElement, ctxTypes, reportMode);
}
}

// Resolve normally
Expand Down Expand Up @@ -383,7 +387,7 @@ export class Resolver extends DiagnosticEmitter {
let numParameters = parameterNodes.length;
let parameterTypes = new Array<Type>(numParameters);
let requiredParameters = 0;
let hasRest = false;
let flags = SignatureFlags.None;
for (let i = 0; i < numParameters; ++i) {
let parameterNode = parameterNodes[i];
switch (parameterNode.parameterKind) {
Expand All @@ -393,7 +397,7 @@ export class Resolver extends DiagnosticEmitter {
}
case ParameterKind.Rest: {
assert(i == numParameters - 1);
hasRest = true;
flags |= SignatureFlags.Rest;
break;
}
}
Expand Down Expand Up @@ -435,7 +439,7 @@ export class Resolver extends DiagnosticEmitter {
);
if (!returnType) return null;
}
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest);
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, flags);
return node.isNullable ? signature.type.asNullable() : signature.type;
}

Expand Down Expand Up @@ -589,6 +593,28 @@ export class Resolver extends DiagnosticEmitter {
return typeArgument.nonNullableType;
}

private resolveBuiltinFirstClassFunctionType(
/** The type to resolve. */
node: NamedTypeNode,
/** Contextual element. */
ctxElement: Element,
/** Contextual types, i.e. `T`. */
ctxTypes: Map<string,Type> | null = null,
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.Report
): Type | null {
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
let signature = typeArgument.getSignature();
// TODO(error)
if (!signature) return null;
signature.setEnv();
signature.thisType = this.program.objectInstance.type;
return typeArgument;
}

/** Resolves a type name to the program element it refers to. */
resolveTypeName(
/** The type name to resolve. */
Expand Down Expand Up @@ -2761,7 +2787,7 @@ export class Resolver extends DiagnosticEmitter {
type,
signatureReference.thisType,
signatureReference.requiredParameters,
signatureReference.hasRest,
signatureReference.flags,
);
}
}
Expand Down
Loading

0 comments on commit 6b35f1e

Please sign in to comment.