Skip to content

Improve Signature lookups, Make Signature immutable #2582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added output.txt
Binary file not shown.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ export class Compiler extends DiagnosticEmitter {
module.setFeatures(featureFlags);

// set up the main start function
let startFunctionInstance = program.makeNativeFunction(BuiltinNames.start, new Signature(program, [], Type.void));
let startFunctionInstance = program.makeNativeFunction(BuiltinNames.start, Signature.new(program, [], Type.void));
startFunctionInstance.internalName = BuiltinNames.start;
this.currentFlow = startFunctionInstance.flow;
this.currentBody = new Array<ExpressionRef>();
Expand Down Expand Up @@ -6430,8 +6430,8 @@ export class Compiler extends DiagnosticEmitter {
assert(operandIndex == minOperands);

// create the varargs stub
stub = original.newStub("varargs");
stub.signature.requiredParameters = maxArguments;
stub = original.newStub("varargs", maxArguments);

original.varargsStub = stub;

// compile initializers of omitted arguments in the scope of the stub,
Expand Down Expand Up @@ -7102,8 +7102,7 @@ export class Compiler extends DiagnosticEmitter {
}
}

let signature = new Signature(this.program, parameterTypes, returnType, thisType);
signature.requiredParameters = numParameters; // !
let signature = Signature.new(this.program, parameterTypes, returnType, thisType, numParameters);
instance = new Function(
prototype.name,
prototype,
Expand Down Expand Up @@ -8669,7 +8668,7 @@ export class Compiler extends DiagnosticEmitter {
)
),
null,
new Signature(this.program, null, classInstance.type, classInstance.type),
Signature.new(this.program, [], classInstance.type, classInstance.type),
contextualTypeArguments
);
}
Expand Down
8 changes: 4 additions & 4 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ export class Program extends DiagnosticEmitter {
/** Managed classes contained in the program, by id. */
managedClasses: Map<i32,Class> = new Map();
/** A set of unique function signatures contained in the program, by id. */
uniqueSignatures: Signature[] = new Array<Signature>(0);
uniqueSignatures: Map<string, Signature> = new Map<string, Signature>();
/** Module exports. */
moduleExports: Map<string,Element> = new Map();
/** Module imports. */
Expand Down Expand Up @@ -3217,7 +3217,7 @@ export class File extends Element {
program.filesByName.set(this.internalName, this);
let startFunction = this.program.makeNativeFunction(
`start:${this.internalName}`,
new Signature(program, null, Type.void),
Signature.new(program, [], Type.void),
this
);
startFunction.internalName = startFunction.name;
Expand Down Expand Up @@ -3826,12 +3826,12 @@ export class Function extends TypedElement {
}

/** Creates a stub for use with this function, i.e. for varargs or override calls. */
newStub(postfix: string): Function {
newStub(postfix: string, requiredParameters: i32 = this.signature.requiredParameters): Function {
let stub = new Function(
this.original.name + STUB_DELIMITER + postfix,
this.prototype,
this.typeArguments,
this.signature.clone(),
this.signature.clone(requiredParameters),
this.contextualTypeArguments
);
stub.original = this.original;
Expand Down
16 changes: 10 additions & 6 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,7 @@ export class Resolver extends DiagnosticEmitter {
);
if (!returnType) return null;
}
let signature = new Signature(this.program, parameterTypes, returnType, thisType);
signature.requiredParameters = requiredParameters;
signature.hasRest = hasRest;
let signature = Signature.new(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let signature = Signature.new(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest);
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest);

return node.isNullable ? signature.type.asNullable() : signature.type;
}

Expand Down Expand Up @@ -2706,7 +2704,14 @@ export class Resolver extends DiagnosticEmitter {
}
const type = this.resolveExpression(expr, tempFlow, ctxType, reportMode);
if (type) {
signatureReference.returnType = type;
functionType.signatureReference = Signature.new(
this.program,
signatureReference.parameterTypes,
type,
signatureReference.thisType,
signatureReference.requiredParameters,
signatureReference.hasRest,
);
}
}
return functionType;
Expand Down Expand Up @@ -2853,8 +2858,7 @@ export class Resolver extends DiagnosticEmitter {
returnType = type;
}

let signature = new Signature(this.program, parameterTypes, returnType, thisType);
signature.requiredParameters = requiredParameters;
let signature = Signature.new(this.program, parameterTypes, returnType, thisType, requiredParameters);

let nameInclTypeParameters = prototype.name;
if (instanceKey.length) nameInclTypeParameters += `<${instanceKey}>`;
Expand Down
124 changes: 72 additions & 52 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,17 @@ export class Type {

/** Tests if this type equals the specified. */
equals(other: Type): bool {
if (this.kind != other.kind) return false;
if (this.kind != other.kind) {
return false;
}
if (this.isReference) {
let selfSignatureReference = this.signatureReference;
let otherSignatureReference = other.signatureReference;

return (
this.classReference == other.classReference &&
this.signatureReference == other.signatureReference &&
this.isNullableReference == other.isNullableReference
this.classReference == other.classReference
&& selfSignatureReference == otherSignatureReference
&& this.isNullableReference == other.isNullableReference
);
}
return true;
Expand Down Expand Up @@ -882,58 +887,72 @@ export function typesToString(types: Type[]): string {

/** Represents a fully resolved function signature. */
export class Signature {
/** Unique id representing this signature. */
id: u32 = 0;
/** Parameter types, if any, excluding `this`. */
parameterTypes: Type[];
/** Number of required parameters excluding `this`. Other parameters are considered optional. */
requiredParameters: i32;
/** Return type. */
returnType: Type;
/** This type, if an instance signature. */
thisType: Type | null;
/** Whether the last parameter is a rest parameter. */
hasRest: bool;
/** Respective function type. */
type: Type;
/** The program that created this signature. */
program: Program;


/** Constructs a new signature. */
constructor(
public static new(
/** The program that created this signature. */
program: Program,
parameterTypes: Type[] | null = null,
returnType: Type | null = null,
thisType: Type | null = null
) {
this.parameterTypes = parameterTypes ? parameterTypes : [];
this.requiredParameters = 0;
this.returnType = returnType ? returnType : Type.void;
this.thisType = thisType;
this.program = program;
this.hasRest = false;
/** Parameter types, if any, excluding `this`. */
parameterTypes: Type[] = [],
/** Return type. */
returnType: Type = Type.void,
/** This type, if an instance signature. */
thisType: Type | null = null,
/** Number of required parameters excluding `this`. Other parameters are considered optional. */
requiredParameters: i32 = parameterTypes ? parameterTypes.length : 0,
/** Whether the last parameter is a rest parameter. */
hasRest: bool = false,
): Signature {
// get the usize type, and the type of the signature
let usizeType = program.options.usizeType;
let type = new Type(
usizeType.kind,
usizeType.flags & ~TypeFlags.Value | TypeFlags.Reference,
usizeType.size
);
this.type = type;
type.signatureReference = this;

// calculate the properties
let signatureTypes = program.uniqueSignatures;
let length = signatureTypes.length;
for (let i = 0; i < length; i++) {
let compare = unchecked(signatureTypes[i]);
if (this.equals(compare)) {
this.id = compare.id;
return this;
}
let nextId = program.nextSignatureId;

// construct the signature and calculate it's unique key
let signature = new Signature(program, parameterTypes, returnType, thisType, requiredParameters, hasRest, nextId, type);
let uniqueKey = signature.toString();

// check if it exists, and return it
if (signatureTypes.has(uniqueKey)) {
let existing = assert(signatureTypes.get(uniqueKey));
assert(signature.equals(existing));
return existing;
}
this.id = program.nextSignatureId++;
signatureTypes.push(this);

// otherwise increment the program's signature id, set the signature reference of the type, and memoize the signature
program.nextSignatureId = nextId + 1;
type.signatureReference = signature;
signatureTypes.set(uniqueKey, signature);
return signature;
}

/** Constructs a new signature. */
private constructor(
/** The program that created this signature. */
public readonly program: Program,
/** Parameter types, if any, excluding `this`. */
public readonly parameterTypes: Type[],
/** Return type. */
public readonly returnType: Type,
/** This type, if an instance signature. */
public readonly thisType: Type | null,
/** Number of required parameters excluding `this`. Other parameters are considered optional. */
public readonly requiredParameters: i32,
/** Whether the last parameter is a rest parameter. */
public readonly hasRest: bool,
/** Unique id representing this signature. */
public readonly id: u32,
/** Respective function type. */
public readonly type: Type,
) {}

get paramRefs(): TypeRef {
let thisType = this.thisType;
let parameterTypes = this.parameterTypes;
Expand Down Expand Up @@ -975,15 +994,15 @@ export class Signature {
if (!this.returnType.equals(other.returnType)) return false;

// check parameter types
let thisParameterTypes = this.parameterTypes;
let selfParameterTypes = this.parameterTypes;
let otherParameterTypes = other.parameterTypes;
let numParameters = thisParameterTypes.length;
if (numParameters != otherParameterTypes.length) return false;
let numParameters = selfParameterTypes.length;
if (numParameters != otherParameterTypes.length) return false;

for (let i = 0; i < numParameters; ++i) {
let thisParameterType = unchecked(thisParameterTypes[i]);
let selfParameterType = unchecked(selfParameterTypes[i]);
let otherParameterType = unchecked(otherParameterTypes[i]);
if (!thisParameterType.equals(otherParameterType)) return false;
if (!selfParameterType.equals(otherParameterType)) return false;
}
return true;
}
Expand Down Expand Up @@ -1105,7 +1124,6 @@ export class Signature {
let thisType = this.thisType;
if (thisType) {
sb.push(validWat ? "this:" : "this: ");
assert(!thisType.signatureReference);
sb.push(thisType.toString(validWat));
index = 1;
}
Expand All @@ -1127,18 +1145,20 @@ export class Signature {
}

/** Creates a clone of this signature that is safe to modify. */
clone(): Signature {
clone(requiredParameters: i32 = this.requiredParameters, hasRest: bool = this.hasRest): Signature {
let parameterTypes = this.parameterTypes;
let numParameterTypes = parameterTypes.length;
let cloneParameterTypes = new Array<Type>(numParameterTypes);
for (let i = 0; i < numParameterTypes; ++i) {
unchecked(cloneParameterTypes[i] = parameterTypes[i]);
}
return new Signature(
return Signature.new(
this.program,
cloneParameterTypes,
this.returnType,
this.thisType
this.thisType,
requiredParameters,
hasRest
);
}
}
8 changes: 4 additions & 4 deletions tests/compiler/builtins.debug.wat
Original file line number Diff line number Diff line change
Expand Up @@ -3213,11 +3213,11 @@
local.set $48
i32.const 0
local.set $49
i32.const 46
i32.const 51
local.set $50
i32.const 47
i32.const 52
local.set $51
i32.const 47
i32.const 52
local.set $52
i32.const 256
local.set $63
Expand Down Expand Up @@ -3262,7 +3262,7 @@
unreachable
end
local.get $50
i32.const 46
i32.const 51
i32.eq
i32.eqz
if
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/builtins.release.wat
Original file line number Diff line number Diff line change
Expand Up @@ -735,9 +735,9 @@
i32.const 5
f64.const 0
f64.const 0
f64.const 46
f64.const 47
f64.const 47
f64.const 51
f64.const 52
f64.const 52
call $~lib/builtins/trace
global.get $~lib/memory/__stack_pointer
local.tee $0
Expand Down