Skip to content

Commit 378659a

Browse files
authored
Speed-up signature lookups, make signatures immutable (#2582)
1 parent 6717de0 commit 378659a

File tree

7 files changed

+99
-76
lines changed

7 files changed

+99
-76
lines changed

Diff for: package-lock.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: src/compiler.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ export class Compiler extends DiagnosticEmitter {
473473
module.setFeatures(featureFlags);
474474

475475
// set up the main start function
476-
let startFunctionInstance = program.makeNativeFunction(BuiltinNames.start, new Signature(program, [], Type.void));
476+
let startFunctionInstance = program.makeNativeFunction(BuiltinNames.start, Signature.create(program, [], Type.void));
477477
startFunctionInstance.internalName = BuiltinNames.start;
478478
this.currentFlow = startFunctionInstance.flow;
479479
this.currentBody = new Array<ExpressionRef>();
@@ -6381,8 +6381,8 @@ export class Compiler extends DiagnosticEmitter {
63816381
assert(operandIndex == minOperands);
63826382

63836383
// create the varargs stub
6384-
stub = original.newStub("varargs");
6385-
stub.signature.requiredParameters = maxArguments;
6384+
stub = original.newStub("varargs", maxArguments);
6385+
63866386
original.varargsStub = stub;
63876387

63886388
// compile initializers of omitted arguments in the scope of the stub,
@@ -7042,8 +7042,7 @@ export class Compiler extends DiagnosticEmitter {
70427042
}
70437043
}
70447044

7045-
let signature = new Signature(this.program, parameterTypes, returnType, thisType);
7046-
signature.requiredParameters = numParameters; // !
7045+
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, numParameters);
70477046
instance = new Function(
70487047
prototype.name,
70497048
prototype,
@@ -8619,7 +8618,7 @@ export class Compiler extends DiagnosticEmitter {
86198618
)
86208619
),
86218620
null,
8622-
new Signature(this.program, null, classInstance.type, classInstance.type),
8621+
Signature.create(this.program, [], classInstance.type, classInstance.type),
86238622
contextualTypeArguments
86248623
);
86258624
}

Diff for: src/program.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ export class Program extends DiagnosticEmitter {
473473
/** Managed classes contained in the program, by id. */
474474
managedClasses: Map<i32,Class> = new Map();
475475
/** A set of unique function signatures contained in the program, by id. */
476-
uniqueSignatures: Signature[] = new Array<Signature>(0);
476+
uniqueSignatures: Map<string, Signature> = new Map<string, Signature>();
477477
/** Module exports. */
478478
moduleExports: Map<string,Element> = new Map();
479479
/** Module imports. */
@@ -3218,7 +3218,7 @@ export class File extends Element {
32183218
program.filesByName.set(this.internalName, this);
32193219
let startFunction = this.program.makeNativeFunction(
32203220
`start:${this.internalName}`,
3221-
new Signature(program, null, Type.void),
3221+
Signature.create(program, [], Type.void),
32223222
this
32233223
);
32243224
startFunction.internalName = startFunction.name;
@@ -3830,12 +3830,12 @@ export class Function extends TypedElement {
38303830
}
38313831

38323832
/** Creates a stub for use with this function, i.e. for varargs or override calls. */
3833-
newStub(postfix: string): Function {
3833+
newStub(postfix: string, requiredParameters: i32 = this.signature.requiredParameters): Function {
38343834
let stub = new Function(
38353835
this.original.name + STUB_DELIMITER + postfix,
38363836
this.prototype,
38373837
this.typeArguments,
3838-
this.signature.clone(),
3838+
this.signature.clone(requiredParameters),
38393839
this.contextualTypeArguments
38403840
);
38413841
stub.original = this.original;

Diff for: src/resolver.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,7 @@ export class Resolver extends DiagnosticEmitter {
422422
);
423423
if (!returnType) return null;
424424
}
425-
let signature = new Signature(this.program, parameterTypes, returnType, thisType);
426-
signature.requiredParameters = requiredParameters;
427-
signature.hasRest = hasRest;
425+
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest);
428426
return node.isNullable ? signature.type.asNullable() : signature.type;
429427
}
430428

@@ -2706,7 +2704,14 @@ export class Resolver extends DiagnosticEmitter {
27062704
}
27072705
const type = this.resolveExpression(expr, tempFlow, ctxType, reportMode);
27082706
if (type) {
2709-
signatureReference.returnType = type;
2707+
functionType.signatureReference = Signature.create(
2708+
this.program,
2709+
signatureReference.parameterTypes,
2710+
type,
2711+
signatureReference.thisType,
2712+
signatureReference.requiredParameters,
2713+
signatureReference.hasRest,
2714+
);
27102715
}
27112716
}
27122717
return functionType;
@@ -2853,8 +2858,7 @@ export class Resolver extends DiagnosticEmitter {
28532858
returnType = type;
28542859
}
28552860

2856-
let signature = new Signature(this.program, parameterTypes, returnType, thisType);
2857-
signature.requiredParameters = requiredParameters;
2861+
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters);
28582862

28592863
let nameInclTypeParameters = prototype.name;
28602864
if (instanceKey.length) nameInclTypeParameters += `<${instanceKey}>`;

Diff for: src/types.ts

+72-53
Original file line numberDiff line numberDiff line change
@@ -421,12 +421,17 @@ export class Type {
421421

422422
/** Tests if this type equals the specified. */
423423
equals(other: Type): bool {
424-
if (this.kind != other.kind) return false;
424+
if (this.kind != other.kind) {
425+
return false;
426+
}
425427
if (this.isReference) {
428+
let selfSignatureReference = this.signatureReference;
429+
let otherSignatureReference = other.signatureReference;
430+
426431
return (
427-
this.classReference == other.classReference &&
428-
this.signatureReference == other.signatureReference &&
429-
this.isNullableReference == other.isNullableReference
432+
this.classReference == other.classReference
433+
&& selfSignatureReference == otherSignatureReference
434+
&& this.isNullableReference == other.isNullableReference
430435
);
431436
}
432437
return true;
@@ -882,58 +887,71 @@ export function typesToString(types: Type[]): string {
882887

883888
/** Represents a fully resolved function signature. */
884889
export class Signature {
885-
/** Unique id representing this signature. */
886-
id: u32 = 0;
887-
/** Parameter types, if any, excluding `this`. */
888-
parameterTypes: Type[];
889-
/** Number of required parameters excluding `this`. Other parameters are considered optional. */
890-
requiredParameters: i32;
891-
/** Return type. */
892-
returnType: Type;
893-
/** This type, if an instance signature. */
894-
thisType: Type | null;
895-
/** Whether the last parameter is a rest parameter. */
896-
hasRest: bool;
897-
/** Respective function type. */
898-
type: Type;
899-
/** The program that created this signature. */
900-
program: Program;
901-
902-
/** Constructs a new signature. */
903-
constructor(
890+
/** Construct a new signature. */
891+
public static create(
892+
/** The program that created this signature. */
904893
program: Program,
905-
parameterTypes: Type[] | null = null,
906-
returnType: Type | null = null,
907-
thisType: Type | null = null
908-
) {
909-
this.parameterTypes = parameterTypes ? parameterTypes : [];
910-
this.requiredParameters = 0;
911-
this.returnType = returnType ? returnType : Type.void;
912-
this.thisType = thisType;
913-
this.program = program;
914-
this.hasRest = false;
894+
/** Parameter types, if any, excluding `this`. */
895+
parameterTypes: Type[] = [],
896+
/** Return type. */
897+
returnType: Type = Type.void,
898+
/** This type, if an instance signature. */
899+
thisType: Type | null = null,
900+
/** Number of required parameters excluding `this`. Other parameters are considered optional. */
901+
requiredParameters: i32 = parameterTypes ? parameterTypes.length : 0,
902+
/** Whether the last parameter is a rest parameter. */
903+
hasRest: bool = false,
904+
): Signature {
905+
// get the usize type, and the type of the signature
915906
let usizeType = program.options.usizeType;
916907
let type = new Type(
917908
usizeType.kind,
918909
usizeType.flags & ~TypeFlags.Value | TypeFlags.Reference,
919910
usizeType.size
920911
);
921-
this.type = type;
922-
type.signatureReference = this;
923912

913+
// calculate the properties
924914
let signatureTypes = program.uniqueSignatures;
925-
let length = signatureTypes.length;
926-
for (let i = 0; i < length; i++) {
927-
let compare = unchecked(signatureTypes[i]);
928-
if (this.equals(compare)) {
929-
this.id = compare.id;
930-
return this;
931-
}
915+
let nextId = program.nextSignatureId;
916+
917+
// construct the signature and calculate it's unique key
918+
let signature = new Signature(program, parameterTypes, returnType, thisType, requiredParameters, hasRest, nextId, type);
919+
let uniqueKey = signature.toString();
920+
921+
// check if it exists, and return it
922+
if (signatureTypes.has(uniqueKey)) {
923+
let existing = assert(signatureTypes.get(uniqueKey));
924+
assert(signature.equals(existing));
925+
return existing;
932926
}
933-
this.id = program.nextSignatureId++;
934-
signatureTypes.push(this);
927+
928+
// otherwise increment the program's signature id, set the signature reference of the type, and memoize the signature
929+
program.nextSignatureId = nextId + 1;
930+
type.signatureReference = signature;
931+
signatureTypes.set(uniqueKey, signature);
932+
return signature;
935933
}
936934

935+
/** Constructs a new signature. */
936+
private constructor(
937+
/** The program that created this signature. */
938+
public readonly program: Program,
939+
/** Parameter types, if any, excluding `this`. */
940+
public readonly parameterTypes: Type[],
941+
/** Return type. */
942+
public readonly returnType: Type,
943+
/** This type, if an instance signature. */
944+
public readonly thisType: Type | null,
945+
/** Number of required parameters excluding `this`. Other parameters are considered optional. */
946+
public readonly requiredParameters: i32,
947+
/** Whether the last parameter is a rest parameter. */
948+
public readonly hasRest: bool,
949+
/** Unique id representing this signature. */
950+
public readonly id: u32,
951+
/** Respective function type. */
952+
public readonly type: Type,
953+
) {}
954+
937955
get paramRefs(): TypeRef {
938956
let thisType = this.thisType;
939957
let parameterTypes = this.parameterTypes;
@@ -975,15 +993,15 @@ export class Signature {
975993
if (!this.returnType.equals(other.returnType)) return false;
976994

977995
// check parameter types
978-
let thisParameterTypes = this.parameterTypes;
996+
let selfParameterTypes = this.parameterTypes;
979997
let otherParameterTypes = other.parameterTypes;
980-
let numParameters = thisParameterTypes.length;
981-
if (numParameters != otherParameterTypes.length) return false;
998+
let numParameters = selfParameterTypes.length;
999+
if (numParameters != otherParameterTypes.length) return false;
9821000

9831001
for (let i = 0; i < numParameters; ++i) {
984-
let thisParameterType = unchecked(thisParameterTypes[i]);
1002+
let selfParameterType = unchecked(selfParameterTypes[i]);
9851003
let otherParameterType = unchecked(otherParameterTypes[i]);
986-
if (!thisParameterType.equals(otherParameterType)) return false;
1004+
if (!selfParameterType.equals(otherParameterType)) return false;
9871005
}
9881006
return true;
9891007
}
@@ -1105,7 +1123,6 @@ export class Signature {
11051123
let thisType = this.thisType;
11061124
if (thisType) {
11071125
sb.push(validWat ? "this:" : "this: ");
1108-
assert(!thisType.signatureReference);
11091126
sb.push(thisType.toString(validWat));
11101127
index = 1;
11111128
}
@@ -1127,18 +1144,20 @@ export class Signature {
11271144
}
11281145

11291146
/** Creates a clone of this signature that is safe to modify. */
1130-
clone(): Signature {
1147+
clone(requiredParameters: i32 = this.requiredParameters, hasRest: bool = this.hasRest): Signature {
11311148
let parameterTypes = this.parameterTypes;
11321149
let numParameterTypes = parameterTypes.length;
11331150
let cloneParameterTypes = new Array<Type>(numParameterTypes);
11341151
for (let i = 0; i < numParameterTypes; ++i) {
11351152
unchecked(cloneParameterTypes[i] = parameterTypes[i]);
11361153
}
1137-
return new Signature(
1154+
return Signature.create(
11381155
this.program,
11391156
cloneParameterTypes,
11401157
this.returnType,
1141-
this.thisType
1158+
this.thisType,
1159+
requiredParameters,
1160+
hasRest
11421161
);
11431162
}
11441163
}

Diff for: tests/compiler/builtins.debug.wat

+4-4
Original file line numberDiff line numberDiff line change
@@ -3223,11 +3223,11 @@
32233223
local.set $48
32243224
i32.const 0
32253225
local.set $49
3226-
i32.const 46
3226+
i32.const 51
32273227
local.set $50
3228-
i32.const 47
3228+
i32.const 52
32293229
local.set $51
3230-
i32.const 47
3230+
i32.const 52
32313231
local.set $52
32323232
i32.const 256
32333233
local.set $63
@@ -3272,7 +3272,7 @@
32723272
unreachable
32733273
end
32743274
local.get $50
3275-
i32.const 46
3275+
i32.const 51
32763276
i32.eq
32773277
i32.eqz
32783278
if

Diff for: tests/compiler/builtins.release.wat

+3-3
Original file line numberDiff line numberDiff line change
@@ -735,9 +735,9 @@
735735
i32.const 5
736736
f64.const 0
737737
f64.const 0
738-
f64.const 46
739-
f64.const 47
740-
f64.const 47
738+
f64.const 51
739+
f64.const 52
740+
f64.const 52
741741
call $~lib/builtins/trace
742742
global.get $~lib/memory/__stack_pointer
743743
local.tee $0

0 commit comments

Comments
 (0)