5cd42e6b21
Binaryen introduced `binaryen.inline` which allows us to tell it inlining hints now, including "never inline" hint (see [0]) This allows us to remove the ugly mangling of wasm function names with `<noInline>` postfix. We also now pass `--strip-toolchain-annotations`: The annotations occupy size in the wasm binary and wasm runtimes ignore them (they are for `wams-opt` only). Except for IR tests: Here we want to see the annotations, so we keep them there. We also rename the package:wasm_builder classes to clearly indicate those are binaryen specific sections. We also make the ir_test.dart put it's options first, allowing the IR tests to override options if needed. [0] https://github.com/WebAssembly/binaryen/commit/3c25487214600a9 Change-Id: I96688bfaeba403a39cd5e7376f8d2889bcbae030 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/510000 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Srujan Gaddam <srujzs@google.com>
235 lines
6.5 KiB
Dart
235 lines
6.5 KiB
Dart
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/core_types.dart';
|
|
import 'package:vm/metadata/direct_call.dart' show DirectCallMetadataRepository;
|
|
import 'package:vm/metadata/inferred_type.dart'
|
|
show
|
|
InferredTypeMetadataRepository,
|
|
InferredReturnTypeMetadataRepository,
|
|
InferredArgTypeMetadataRepository;
|
|
import 'package:vm/metadata/procedure_attributes.dart'
|
|
show ProcedureAttributesMetadataRepository;
|
|
import 'package:vm/metadata/table_selector.dart'
|
|
show TableSelectorMetadataRepository;
|
|
import 'package:vm/metadata/unreachable.dart';
|
|
|
|
final bool compilerAssertsEnabled = (() {
|
|
bool compilerAsserts = false;
|
|
assert(compilerAsserts = true);
|
|
return compilerAsserts;
|
|
})();
|
|
|
|
bool hasPragma(CoreTypes coreTypes, Annotatable node, String name) {
|
|
return getPragma(coreTypes, node, name, defaultValue: '') != null;
|
|
}
|
|
|
|
T? getPragma<T>(
|
|
CoreTypes coreTypes,
|
|
Annotatable node,
|
|
String name, {
|
|
T? defaultValue,
|
|
}) {
|
|
for (Expression annotation in node.annotations) {
|
|
if (annotation is ConstantExpression) {
|
|
Constant constant = annotation.constant;
|
|
if (constant is InstanceConstant) {
|
|
if (constant.classNode == coreTypes.pragmaClass) {
|
|
Constant? nameConstant =
|
|
constant.fieldValues[coreTypes.pragmaName.fieldReference];
|
|
if (nameConstant is StringConstant && nameConstant.value == name) {
|
|
Constant? value =
|
|
constant.fieldValues[coreTypes.pragmaOptions.fieldReference];
|
|
if (value == null || value is NullConstant) {
|
|
return defaultValue;
|
|
}
|
|
if (value is PrimitiveConstant<T>) {
|
|
return value.value;
|
|
}
|
|
if (value is! T) {
|
|
throw ArgumentError(
|
|
"$name pragma argument has unexpected type "
|
|
"${value.runtimeType} (expected $T)",
|
|
);
|
|
}
|
|
return value as T;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
bool hasWasmImportPragma(CoreTypes coreTypes, Member member) {
|
|
return hasPragma(coreTypes, member, "wasm:import");
|
|
}
|
|
|
|
ImportName? getWasmImportPragma(CoreTypes coreTypes, Member member) {
|
|
String? importName = getPragma(coreTypes, member, "wasm:import");
|
|
if (importName != null) {
|
|
int dot = importName.indexOf('.');
|
|
if (dot != -1) {
|
|
assert(!member.isInstanceMember);
|
|
String module = importName.substring(0, dot);
|
|
String name = importName.substring(dot + 1);
|
|
return ImportName(module, name);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
final class ImportName {
|
|
final String moduleName;
|
|
final String itemName;
|
|
|
|
ImportName(this.moduleName, this.itemName);
|
|
|
|
@override
|
|
String toString() {
|
|
return '$moduleName.$itemName';
|
|
}
|
|
}
|
|
|
|
bool hasWasmExportPragma(CoreTypes coreTypes, Member member) {
|
|
return hasPragma(coreTypes, member, "wasm:export");
|
|
}
|
|
|
|
bool hasWasmWeakExportPragma(CoreTypes coreTypes, Member member) {
|
|
return hasPragma(coreTypes, member, "wasm:weak-export");
|
|
}
|
|
|
|
bool? getWasmPreferInlinePragma(CoreTypes coreTypes, Member member) {
|
|
return getPragma<bool>(
|
|
coreTypes,
|
|
member,
|
|
"wasm:prefer-inline",
|
|
defaultValue: true,
|
|
);
|
|
}
|
|
|
|
bool? getWasmNeverInlinePragma(CoreTypes coreTypes, Member member) {
|
|
return getPragma<bool>(
|
|
coreTypes,
|
|
member,
|
|
"wasm:never-inline",
|
|
defaultValue: true,
|
|
);
|
|
}
|
|
|
|
String? getWasmExportPragma(CoreTypes coreTypes, Member member) {
|
|
return getPragma<String>(
|
|
coreTypes,
|
|
member,
|
|
'wasm:export',
|
|
defaultValue: member.name.text,
|
|
);
|
|
}
|
|
|
|
String? getWasmWeakExportPragma(CoreTypes coreTypes, Member member) {
|
|
return getPragma<String>(
|
|
coreTypes,
|
|
member,
|
|
'wasm:weak-export',
|
|
defaultValue: member.name.text,
|
|
);
|
|
}
|
|
|
|
bool hasWasmPureFunctionPragma(CoreTypes coreTypes, Member member) {
|
|
return getPragma<bool>(
|
|
coreTypes,
|
|
member,
|
|
'wasm:pure-function',
|
|
defaultValue: true,
|
|
) ==
|
|
true;
|
|
}
|
|
|
|
/// Add a `@pragma('wasm:entry-point')` annotation to an annotatable.
|
|
T addWasmEntryPointPragma<T extends Annotatable>(T node, CoreTypes coreTypes) =>
|
|
addPragma(node, 'wasm:entry-point', coreTypes);
|
|
|
|
T addPragma<T extends Annotatable>(
|
|
T node,
|
|
String pragmaName,
|
|
CoreTypes coreTypes, {
|
|
Constant? value,
|
|
}) => node
|
|
..addAnnotation(
|
|
ConstantExpression(
|
|
InstanceConstant(coreTypes.pragmaClass.reference, [], {
|
|
coreTypes.pragmaName.fieldReference: StringConstant(pragmaName),
|
|
coreTypes.pragmaOptions.fieldReference: value ?? NullConstant(),
|
|
}),
|
|
),
|
|
);
|
|
|
|
List<int> _intToLittleEndianBytes(int i) {
|
|
List<int> bytes = [];
|
|
bytes.add(i & 0xFF);
|
|
i >>>= 8;
|
|
while (i != 0) {
|
|
bytes.add(i & 0xFF);
|
|
i >>>= 8;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
String intToBase64(int i) => base64.encode(_intToLittleEndianBytes(i));
|
|
|
|
/// Maps ints to minimal length strings.
|
|
///
|
|
/// For simplicity, this only uses combinations of 1-byte characters. The 2+
|
|
/// byte characters don't significantly impact the average string size.
|
|
///
|
|
/// Will not emit an empty string.
|
|
String intToMinString(int i) {
|
|
// Stick to the 92 printable characters (starting after "), from 35 to 126.
|
|
var base = 92;
|
|
assert(i >= 0);
|
|
i += 1;
|
|
final codeUnits = <int>[];
|
|
while (i > 0) {
|
|
int remainder = i % base;
|
|
i ~/= base;
|
|
codeUnits.add(remainder + 35);
|
|
}
|
|
return String.fromCharCodes(codeUnits);
|
|
}
|
|
|
|
/// Maps ints to minimal length strings that are safe to use as JavaScript
|
|
/// identifiers.
|
|
///
|
|
/// Will not emit an empty string.
|
|
String intToMinJsSafeString(int i) {
|
|
assert(i >= 0);
|
|
i += 1;
|
|
final codeUnits = <int>[];
|
|
while (i > 0) {
|
|
int remainder = i % 52;
|
|
i ~/= 52;
|
|
if (remainder < 26) {
|
|
codeUnits.add(remainder + 65); // 'A'-'Z'
|
|
} else {
|
|
codeUnits.add(remainder - 26 + 97); // 'a'-'z'
|
|
}
|
|
}
|
|
return String.fromCharCodes(codeUnits);
|
|
}
|
|
|
|
Component createEmptyComponent() {
|
|
return Component()
|
|
..addMetadataRepository(UnreachableNodeMetadataRepository())
|
|
..addMetadataRepository(ProcedureAttributesMetadataRepository())
|
|
..addMetadataRepository(TableSelectorMetadataRepository())
|
|
..addMetadataRepository(DirectCallMetadataRepository())
|
|
..addMetadataRepository(InferredTypeMetadataRepository())
|
|
..addMetadataRepository(InferredReturnTypeMetadataRepository())
|
|
..addMetadataRepository(InferredArgTypeMetadataRepository());
|
|
}
|