Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
53 changes: 6 additions & 47 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
"llparse-test-fixture": "^5.0.1",
"mocha": "^9.2.2",
"ts-node": "^9.0.0",
"typescript": "^4.0.3"
"typescript": "^5.0.3"
Copy link
Member Author

Choose a reason for hiding this comment

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

The package wasn't compiling without typescript update.

},
"dependencies": {
"debug": "^4.2.0",
"llparse-frontend": "^3.0.0"
}
}
}
23 changes: 14 additions & 9 deletions src/implementation/c/compilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,9 @@ export class Compilation {
const hex: string[] = [];
for (let j = i; j < limit; j++) {
const value = buffer[j];
assert(value !== undefined);
Copy link
Member Author

Choose a reason for hiding this comment

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

With typescript version change - new errors popped up.


const ch = String.fromCharCode(value);
// `'`, `\`
if (value === 0x27 || value === 0x5c) {
hex.push(`'\\${ch}'`);
} else if (value >= 0x20 && value <= 0x7e) {
hex.push(`'${ch}'`);
} else {
hex.push(`0x${value.toString(16)}`);
}
hex.push(this.toChar(value));
}
let line = ' ' + hex.join(', ');
if (limit !== buffer.length) {
Expand Down Expand Up @@ -331,4 +324,16 @@ export class Compilation {
});
return res;
}

public toChar(value: number): string {
const ch = String.fromCharCode(value);
// `'`, `\`
if (value === 0x27 || value === 0x5c) {
return `'\\${ch}'`;
} else if (value >= 0x20 && value <= 0x7e) {
return `'${ch}'`;
} else {
return `0x${value.toString(16)}`;
}
}
}
5 changes: 5 additions & 0 deletions src/implementation/c/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export class CCompiler {
out.push('#endif /* __SSE4_2__ */');
out.push('');

out.push('#ifdef __wasm__');
out.push(' #include <wasm_simd128.h>');
out.push('#endif /* __wasm__ */');
out.push('');

out.push('#ifdef _MSC_VER');
out.push(' #define ALIGN(n) _declspec(align(n))');
out.push('#else /* !_MSC_VER */');
Expand Down
109 changes: 102 additions & 7 deletions src/implementation/c/node/table-lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const SSE_RANGES_LEN = 16;
// _mm_cmpestri takes 128bit input
const SSE_RANGES_PAD = 16;
const MAX_SSE_CALLS = 2;
const MAX_WASM_RANGES = 6;
const SSE_ALIGNMENT = 16;

interface ITable {
Expand All @@ -34,7 +35,10 @@ export class TableLookup extends Node<frontend.node.TableLookup> {
// Try to vectorize nodes matching characters and looping to themselves
// NOTE: `switch` below triggers when there is not enough characters in the
// stream for vectorized processing.
this.buildSSE(out);
if (this.canVectorize()) {
this.buildSSE(out);
this.buildWASM(out);
}

const current = transform.build(ctx, `*${ctx.posArg()}`);
out.push(`switch (${table.name}[(uint8_t) ${current}]) {`);
Expand Down Expand Up @@ -63,9 +67,7 @@ export class TableLookup extends Node<frontend.node.TableLookup> {
out.push('}');
}

private buildSSE(out: string[]): boolean {
const ctx = this.compilation;

private canVectorize(): boolean {
// Transformation is not supported atm
if (this.ref.transform && this.ref.transform.ref.name !== 'id') {
return false;
Expand All @@ -83,8 +85,14 @@ export class TableLookup extends Node<frontend.node.TableLookup> {
return false;
}

assert.strictEqual(edge.noAdvance, false);

return true;
}

private buildRanges(edge: frontend.node.TableLookup["edges"][0]): number[] {
// NOTE: keys are sorted
let ranges: number[] = [];
const ranges: number[] = [];
let first: number | undefined;
let last: number | undefined;
for (const key of edge.keys) {
Expand All @@ -104,6 +112,16 @@ export class TableLookup extends Node<frontend.node.TableLookup> {
if (first !== undefined && last !== undefined) {
ranges.push(first, last);
}
return ranges;
}

private buildSSE(out: string[]): boolean {
const ctx = this.compilation;

const edge = this.ref.edges[0];
assert(edge !== undefined);

const ranges = this.buildRanges(edge);

if (ranges.length === 0) {
return false;
Expand All @@ -118,7 +136,6 @@ export class TableLookup extends Node<frontend.node.TableLookup> {
out.push(`if (${ctx.endPosArg()} - ${ctx.posArg()} >= 16) {`);
out.push(' __m128i ranges;');
out.push(' __m128i input;');
out.push(' int avail;');
Copy link
Member Author

Choose a reason for hiding this comment

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

Unused.

out.push(' int match_len;');
out.push('');
out.push(' /* Load input */');
Expand All @@ -145,7 +162,6 @@ export class TableLookup extends Node<frontend.node.TableLookup> {
out.push(` ${ctx.posArg()} += match_len;`);

const tmp: string[] = [];
assert.strictEqual(edge.noAdvance, false);
this.tailTo(tmp, {
noAdvance: true,
node: edge.node,
Expand All @@ -167,6 +183,85 @@ export class TableLookup extends Node<frontend.node.TableLookup> {
return true;
}

private buildWASM(out: string[]): boolean {
const ctx = this.compilation;

const edge = this.ref.edges[0];
assert(edge !== undefined);

const ranges = this.buildRanges(edge);

if (ranges.length === 0) {
return false;
}

// Way too many calls would be required
if (ranges.length > MAX_WASM_RANGES) {
return false;
}

out.push('#ifdef __wasm_simd128__');
out.push(`if (${ctx.endPosArg()} - ${ctx.posArg()} >= 16) {`);
out.push(' v128_t input;');
out.push(' v128_t mask;');
out.push(' v128_t single;');
out.push(' int match_len;');
out.push('');
out.push(' /* Load input */');
out.push(` input = wasm_v128_load(${ctx.posArg()});`);

out.push(' /* Find first character that does not match `ranges` */');
function v128(value: number): string {
return `wasm_u8x16_const_splat(${ctx.toChar(value)})`;
}

for (let off = 0; off < ranges.length; off += 2) {
const start = ranges[off];
const end = ranges[off + 1];
assert(start !== undefined);
assert(end !== undefined);

// Same character, equality is sufficient (and faster)
if (start === end) {
out.push(` single = wasm_i8x16_eq(input, ${v128(start)});`);
} else {
out.push(` single = wasm_v128_and(`);
out.push(` wasm_i8x16_ge(input, ${v128(start)}),`);
out.push(` wasm_i8x16_le(input, ${v128(end)})`);
out.push(' );');
}

if (off === 0) {
out.push(' mask = single;');
} else {
out.push(' mask = wasm_v128_or(mask, single);');
}
}
out.push(' match_len = __builtin_ctz(');
out.push(' ~wasm_i8x16_bitmask(mask)');
out.push(' );');
out.push(` ${ctx.posArg()} += match_len;`);
out.push(' if (match_len != 16) {');
{
const tmp: string[] = [];
this.tailTo(tmp, this.ref.otherwise!);
ctx.indent(out, tmp, ' ');
}
out.push(' }');

const tmp: string[] = [];
this.tailTo(tmp, {
noAdvance: true,
node: edge.node,
});
ctx.indent(out, tmp, ' ');
out.push('}');

out.push('#endif /* __wasm_simd128__ */');

return true;
}

private buildTable(): ITable {
const table: number[] = new Array(MAX_CHAR + 1).fill(0);

Expand Down