Skip to content

Commit 242704c

Browse files
WasmParser: Specialize more hot paths
With this change, we can achieve 17% speedup on stringify macro benchmark. The performance number is same as the case with CMO but with smaller binary size. | | baseline | CMO | this change | |------------|----------|-------|-------------| | speed (ms) | 198.1 | 172.8 | 169.9 | | size (kb) | 1739 | 1923 | 1797 |
1 parent a0886be commit 242704c

File tree

4 files changed

+96
-21
lines changed

4 files changed

+96
-21
lines changed

Sources/WasmParser/ParsingLimits.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/// Limits for parsing WebAssembly modules.
2+
@usableFromInline
23
struct ParsingLimits {
34
/// Maximum number of locals in a function.
5+
@usableFromInline
46
var maxFunctionLocals: UInt64
57

68
/// The default limits for parsing.

Sources/WasmParser/Stream/Stream.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@usableFromInline
12
enum StreamError<Element>: Swift.Error, Equatable where Element: Hashable {
23
case unexpectedEnd(expected: Set<Element>?)
34
case unexpected(Element, index: Int, expected: Set<Element>?)

Sources/WasmParser/WasmParser.swift

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ import struct SystemPackage.FilePath
1313
public struct Parser<Stream: ByteStream> {
1414
@usableFromInline
1515
let stream: Stream
16-
let limits: ParsingLimits
17-
var orderTracking = OrderTracking()
16+
@usableFromInline let limits: ParsingLimits
17+
@usableFromInline var orderTracking = OrderTracking()
1818

19+
@usableFromInline
1920
enum NextParseTarget {
2021
case header
2122
case section
2223
}
23-
private var nextParseTarget: NextParseTarget
24+
@usableFromInline
25+
var nextParseTarget: NextParseTarget
2426

2527
public let features: WasmFeatureSet
2628
public var offset: Int {
@@ -184,11 +186,14 @@ public struct WasmFeatureSet: OptionSet {
184186
}
185187

186188
/// The WebAssembly memory64 proposal
187-
public static let memory64 = WasmFeatureSet(rawValue: 1 << 0)
189+
@_alwaysEmitIntoClient
190+
public static var memory64: WasmFeatureSet { WasmFeatureSet(rawValue: 1 << 0) }
188191
/// The WebAssembly reference types proposal
189-
public static let referenceTypes = WasmFeatureSet(rawValue: 1 << 1)
192+
@_alwaysEmitIntoClient
193+
public static var referenceTypes: WasmFeatureSet { WasmFeatureSet(rawValue: 1 << 1) }
190194
/// The WebAssembly threads proposal
191-
public static let threads = WasmFeatureSet(rawValue: 1 << 2)
195+
@_alwaysEmitIntoClient
196+
public static var threads: WasmFeatureSet { WasmFeatureSet(rawValue: 1 << 2) }
192197

193198
/// The default feature set
194199
public static let `default`: WasmFeatureSet = [.referenceTypes]
@@ -224,10 +229,12 @@ extension WasmParserError: CustomStringConvertible {
224229
}
225230

226231
extension WasmParserError.Message {
232+
@usableFromInline
227233
static func invalidMagicNumber(_ bytes: [UInt8]) -> Self {
228234
Self("magic header not detected: expected \(WASM_MAGIC) but got \(bytes)")
229235
}
230236

237+
@usableFromInline
231238
static func unknownVersion(_ bytes: [UInt8]) -> Self {
232239
Self("unknown binary version: \(bytes)")
233240
}
@@ -236,11 +243,13 @@ extension WasmParserError.Message {
236243
Self("malformed UTF-8 encoding: \(bytes)")
237244
}
238245

246+
@usableFromInline
239247
static func invalidSectionSize(_ size: UInt32) -> Self {
240248
// TODO: Remove size parameter
241249
Self("unexpected end-of-file")
242250
}
243251

252+
@usableFromInline
244253
static func malformedSectionID(_ id: UInt8) -> Self {
245254
Self("malformed section id: \(id)")
246255
}
@@ -249,6 +258,7 @@ extension WasmParserError.Message {
249258
Self("Zero expected but got \(actual)")
250259
}
251260

261+
@usableFromInline
252262
static func tooManyLocals(_ count: UInt64, limit: UInt64) -> Self {
253263
Self("Too many locals: \(count) vs \(limit)")
254264
}
@@ -262,17 +272,21 @@ extension WasmParserError.Message {
262272
return Self("Unimplemented instruction: \(opcode)\(suffixText)")
263273
}
264274

275+
@usableFromInline
265276
static func unexpectedElementKind(expected: UInt32, actual: UInt32) -> Self {
266277
Self("Unexpected element kind: expected \(expected) but got \(actual)")
267278
}
268279

280+
@usableFromInline
269281
static let integerRepresentationTooLong = Self("Integer representation is too long")
270282

271283
@usableFromInline
272284
static let endOpcodeExpected = Self("`end` opcode expected but not found")
273285

286+
@usableFromInline
274287
static let unexpectedEnd = Self("Unexpected end of the stream")
275288

289+
@usableFromInline
276290
static func sectionSizeMismatch(expected: Int, actual: Int) -> Self {
277291
Self("Section size mismatch: expected \(expected) but got \(actual)")
278292
}
@@ -281,23 +295,27 @@ extension WasmParserError.Message {
281295
Self("Illegal opcode: \(opcode)")
282296
}
283297

298+
@usableFromInline
284299
static func malformedMutability(_ byte: UInt8) -> Self {
285300
Self("Malformed mutability: \(byte)")
286301
}
287302

303+
@usableFromInline
288304
static func malformedFunctionType(_ byte: UInt8) -> Self {
289305
Self("Malformed function type: \(byte)")
290306
}
291307

308+
@usableFromInline
292309
static let sectionOutOfOrder = Self("Sections in the module are out of order")
293310

311+
@usableFromInline
294312
static func malformedLimit(_ byte: UInt8) -> Self {
295313
Self("Malformed limit: \(byte)")
296314
}
297315

298316
@usableFromInline static let malformedIndirectCall = Self("Malformed indirect call")
299317

300-
static func malformedDataSegmentKind(_ kind: UInt32) -> Self {
318+
@usableFromInline static func malformedDataSegmentKind(_ kind: UInt32) -> Self {
301319
Self("Malformed data segment kind: \(kind)")
302320
}
303321

@@ -309,7 +327,8 @@ extension WasmParserError.Message {
309327
/// > Note:
310328
/// <https://webassembly.github.io/spec/core/binary/conventions.html#vectors>
311329
extension ByteStream {
312-
fileprivate func parseVector<Content>(content parser: () throws -> Content) throws -> [Content] {
330+
@inlinable
331+
func parseVector<Content>(content parser: () throws -> Content) throws -> [Content] {
313332
var contents = [Content]()
314333
let count: UInt32 = try parseUnsigned()
315334
for _ in 0..<count {
@@ -359,7 +378,7 @@ extension ByteStream {
359378
}
360379

361380
extension Parser {
362-
@usableFromInline
381+
@inlinable
363382
func parseVector<Content>(content parser: () throws -> Content) throws -> [Content] {
364383
try stream.parseVector(content: parser)
365384
}
@@ -426,7 +445,7 @@ extension Parser {
426445

427446
/// > Note:
428447
/// <https://webassembly.github.io/spec/core/binary/types.html#result-types>
429-
@usableFromInline
448+
@inlinable
430449
func parseResultType() throws -> BlockType {
431450
guard let nextByte = try stream.peek() else {
432451
throw makeError(.unexpectedEnd)
@@ -444,6 +463,7 @@ extension Parser {
444463

445464
/// > Note:
446465
/// <https://webassembly.github.io/spec/core/binary/types.html#function-types>
466+
@inlinable
447467
func parseFunctionType() throws -> FunctionType {
448468
let opcode = try stream.consumeAny()
449469

@@ -463,6 +483,7 @@ extension Parser {
463483

464484
/// > Note:
465485
/// <https://webassembly.github.io/spec/core/binary/types.html#limits>
486+
@usableFromInline
466487
func parseLimits() throws -> Limits {
467488
let b = try stream.consumeAny()
468489
let sharedMask: UInt8 = 0b0010
@@ -508,6 +529,7 @@ extension Parser {
508529

509530
/// > Note:
510531
/// <https://webassembly.github.io/spec/core/binary/types.html#table-types>
532+
@inlinable
511533
func parseTableType() throws -> TableType {
512534
let elementType: ReferenceType
513535
let b = try stream.consumeAny()
@@ -527,12 +549,14 @@ extension Parser {
527549

528550
/// > Note:
529551
/// <https://webassembly.github.io/spec/core/binary/types.html#global-types>
552+
@inlinable
530553
func parseGlobalType() throws -> GlobalType {
531554
let valueType = try parseValueType()
532555
let mutability = try parseMutability()
533556
return GlobalType(mutability: mutability, valueType: valueType)
534557
}
535558

559+
@inlinable
536560
func parseMutability() throws -> Mutability {
537561
let b = try stream.consumeAny()
538562
switch b {
@@ -547,14 +571,14 @@ extension Parser {
547571

548572
/// > Note:
549573
/// <https://webassembly.github.io/spec/core/binary/instructions.html#memory-instructions>
550-
@usableFromInline
574+
@inlinable
551575
func parseMemarg() throws -> MemArg {
552576
let align: UInt32 = try parseUnsigned()
553577
let offset: UInt64 = try features.contains(.memory64) ? parseUnsigned(UInt64.self) : UInt64(parseUnsigned(UInt32.self))
554578
return MemArg(offset: offset, align: align)
555579
}
556580

557-
func parseVectorBytes() throws -> ArraySlice<UInt8> {
581+
@inlinable func parseVectorBytes() throws -> ArraySlice<UInt8> {
558582
let count: UInt32 = try parseUnsigned()
559583
return try stream.consume(count: Int(count))
560584
}
@@ -891,14 +915,19 @@ extension Parser {
891915
}
892916
}
893917

918+
@usableFromInline
894919
struct InstructionFactory: AnyInstructionVisitor {
895-
var insts: [Instruction] = []
920+
@usableFromInline var insts: [Instruction] = []
921+
922+
@inlinable init() {}
896923

924+
@inlinable
897925
mutating func visit(_ instruction: Instruction) throws {
898926
insts.append(instruction)
899927
}
900928
}
901929

930+
@usableFromInline
902931
func parseConstExpression() throws -> ConstExpression {
903932
var factory = InstructionFactory()
904933
var inst: InstructionCode
@@ -914,6 +943,7 @@ extension Parser {
914943
extension Parser {
915944
/// > Note:
916945
/// <https://webassembly.github.io/spec/core/binary/modules.html#custom-section>
946+
@usableFromInline
917947
func parseCustomSection(size: UInt32) throws -> CustomSection {
918948
let preNameIndex = stream.currentIndex
919949
let name = try parseName()
@@ -931,12 +961,14 @@ extension Parser {
931961

932962
/// > Note:
933963
/// <https://webassembly.github.io/spec/core/binary/modules.html#type-section>
964+
@inlinable
934965
func parseTypeSection() throws -> [FunctionType] {
935966
return try parseVector { try parseFunctionType() }
936967
}
937968

938969
/// > Note:
939970
/// <https://webassembly.github.io/spec/core/binary/modules.html#import-section>
971+
@usableFromInline
940972
func parseImportSection() throws -> [Import] {
941973
return try parseVector {
942974
let module = try parseName()
@@ -962,24 +994,28 @@ extension Parser {
962994

963995
/// > Note:
964996
/// <https://webassembly.github.io/spec/core/binary/modules.html#function-section>
997+
@inlinable
965998
func parseFunctionSection() throws -> [TypeIndex] {
966999
return try parseVector { try parseUnsigned() }
9671000
}
9681001

9691002
/// > Note:
9701003
/// <https://webassembly.github.io/spec/core/binary/modules.html#table-section>
1004+
@usableFromInline
9711005
func parseTableSection() throws -> [Table] {
9721006
return try parseVector { try Table(type: parseTableType()) }
9731007
}
9741008

9751009
/// > Note:
9761010
/// <https://webassembly.github.io/spec/core/binary/modules.html#memory-section>
1011+
@usableFromInline
9771012
func parseMemorySection() throws -> [Memory] {
9781013
return try parseVector { try Memory(type: parseLimits()) }
9791014
}
9801015

9811016
/// > Note:
9821017
/// <https://webassembly.github.io/spec/core/binary/modules.html#global-section>
1018+
@usableFromInline
9831019
func parseGlobalSection() throws -> [Global] {
9841020
return try parseVector {
9851021
let type = try parseGlobalType()
@@ -990,6 +1026,7 @@ extension Parser {
9901026

9911027
/// > Note:
9921028
/// <https://webassembly.github.io/spec/core/binary/modules.html#export-section>
1029+
@usableFromInline
9931030
func parseExportSection() throws -> [Export] {
9941031
return try parseVector {
9951032
let name = try parseName()
@@ -1014,12 +1051,14 @@ extension Parser {
10141051

10151052
/// > Note:
10161053
/// <https://webassembly.github.io/spec/core/binary/modules.html#start-section>
1054+
@usableFromInline
10171055
func parseStartSection() throws -> FunctionIndex {
10181056
return try parseUnsigned()
10191057
}
10201058

10211059
/// > Note:
10221060
/// <https://webassembly.github.io/spec/core/binary/modules.html#element-section>
1061+
@inlinable
10231062
func parseElementSection() throws -> [ElementSegment] {
10241063
return try parseVector {
10251064
let flag = try ElementSegment.Flag(rawValue: parseUnsigned())
@@ -1081,6 +1120,7 @@ extension Parser {
10811120

10821121
/// > Note:
10831122
/// <https://webassembly.github.io/spec/core/binary/modules.html#code-section>
1123+
@inlinable
10841124
func parseCodeSection() throws -> [Code] {
10851125
return try parseVector {
10861126
let size = try parseUnsigned() as UInt32
@@ -1111,6 +1151,7 @@ extension Parser {
11111151

11121152
/// > Note:
11131153
/// <https://webassembly.github.io/spec/core/binary/modules.html#data-section>
1154+
@inlinable
11141155
func parseDataSection() throws -> [DataSegment] {
11151156
return try parseVector {
11161157
let kind: UInt32 = try parseUnsigned()
@@ -1136,6 +1177,7 @@ extension Parser {
11361177

11371178
/// > Note:
11381179
/// <https://webassembly.github.io/spec/core/binary/modules.html#data-count-section>
1180+
@usableFromInline
11391181
func parseDataCountSection() throws -> UInt32 {
11401182
return try parseUnsigned()
11411183
}
@@ -1163,6 +1205,7 @@ public enum ParsingPayload {
11631205
extension Parser {
11641206
/// > Note:
11651207
/// <https://webassembly.github.io/spec/core/binary/modules.html#binary-magic>
1208+
@usableFromInline
11661209
func parseMagicNumber() throws {
11671210
let magicNumber = try stream.consume(count: 4)
11681211
guard magicNumber.elementsEqual(WASM_MAGIC) else {
@@ -1172,6 +1215,7 @@ extension Parser {
11721215

11731216
/// > Note:
11741217
/// <https://webassembly.github.io/spec/core/binary/modules.html#binary-version>
1218+
@usableFromInline
11751219
func parseVersion() throws -> [UInt8] {
11761220
let version = try Array(stream.consume(count: 4))
11771221
guard version == [0x01, 0x00, 0x00, 0x00] else {
@@ -1180,7 +1224,9 @@ extension Parser {
11801224
return version
11811225
}
11821226

1227+
@usableFromInline
11831228
struct OrderTracking {
1229+
@usableFromInline
11841230
enum Order: UInt8 {
11851231
case initial = 0
11861232
case type
@@ -1198,7 +1244,10 @@ extension Parser {
11981244
case data
11991245
}
12001246

1201-
private var last: Order = .initial
1247+
@usableFromInline
1248+
var last: Order = .initial
1249+
1250+
@inlinable
12021251
mutating func track(order: Order, parser: Parser) throws {
12031252
guard last.rawValue < order.rawValue else {
12041253
throw parser.makeError(.sectionOutOfOrder)
@@ -1235,6 +1284,7 @@ extension Parser {
12351284
/// }
12361285
/// }
12371286
/// ```
1287+
@inlinable
12381288
public mutating func parseNext() throws -> ParsingPayload? {
12391289
switch nextParseTarget {
12401290
case .header:

0 commit comments

Comments
 (0)