') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - foltik/SignalScript: A compiler and DSL which uses MLIR to compile stateful signal processing kernels to vectorized assembly · GitHub
Skip to content

Repository files navigation

SignalScript

SignalScript uses MLIR to compile stateful signal processing kernels to vectorized assembly. The compiler parses source code into an AST, lowers it to standard MLIR dialects (func, arith, memref, affine), and then uses LLVM to produce optimized assembly.

From a single pointwise kernel, SignalScript automatically generates variants which are batched across instruments, time, or both. In each variant, state is automatically laid out for optimal vectorization and data locality. With modest effort, SignalScript could generate Cuda or SPIR-V GPU kernels with the same approach.

Usage

nix develop
make
./build/ssc --help

Example

fnema(curr,alpha){
state prev;
next = (alpha * curr) + ((1.0 - alpha)* prev);
prev = next;return next;}fnrate_of_change(curr){
state prev;
delta = curr - prev;
prev = curr;return delta;}fnpipeline(x){
y = rate_of_change(x);
z = ema(y,0.9);return z;}

Scalar Mode (default)

By default, SignalScript generates a function that runs the kernel on one input value, returning one output value. It has the signature (float input, float[S] state) -> float, where S is the total size of the state declared within the kernel.

./build/ssc test/kernels.ss --fn=pipeline --avx512

This results in the following assembly, where:

  • input: xmm0
  • rate_of_change.prev: [rdi]
  • ema.prev: [rdi + 4]
  • output: xmm0
pipeline: vsubss xmm1,xmm0, dword ptr [rdi]vmovss dword ptr [rdi],xmm0 vmulss xmm0,xmm1, dword ptr [rip+ .LCPI0_0]vmovssxmm1, dword ptr [rdi+4] vmulss xmm1,xmm1, dword ptr [rip+ .LCPI0_1] vaddss xmm0,xmm0,xmm1vmovss dword ptr [rdi+4],xmm0ret.LCPI0_0: .long 0x3f666666 #float0.899999976.LCPI0_1: .long 0x3dccccd0 #float0.100000024

Batched Mode

In batched mode (--batch=B), SignalScript generates a function that runs B instances of the original kernel in parallel, writing the outputs to a buffer. It has the signature (float input, float[S,B] state, float[B] output) -> ().

State variables are automatically converted to Structure-of-Arrays form (float[S, B] rather than float[B, S]) such that each individual state variable is contiguous in memory across all batches, allowing vectorization.

./build/ssc test/kernels.ss --fn=pipeline --batch=16 --avx512

This results in the following assembly, where:

  • input[16]: zmm0
  • rate_of_change.prev[16]: [rsi]
  • ema.prev[16]: [rsi + 64]
  • output[16]: [rdx]

Notice the computation of all 16 instances of the kernel in parallel via the 512 bit zmm registers.

pipeline_batched:vmovupszmm0, zmmword ptr [rdi] vsubps zmm1,zmm0, zmmword ptr [rsi]vmovups zmmword ptr [rsi],zmm0 vmulps zmm0,zmm1, dword ptr [rip+ .LCPI0_0]{1to16}vmovupszmm1, zmmword ptr [rsi+64] vmulps zmm1,zmm1, dword ptr [rip+ .LCPI0_1]{1to16} vaddps zmm0,zmm0,zmm1vmovups zmmword ptr [rsi+64],zmm0vmovups zmmword ptr [rdx],zmm0vzeroupperret.LCPI0_0: .long 0x3f666666 #float0.899999976.LCPI0_1: .long 0x3dccccd0 #float0.100000024

SignalScript can also generate Arm Neon instructions.

./build/ssc test/kernels.ss --fn=pipeline --batch=4 --neon
pipeline_batched:mov w8, #26214 movk w8, #16230,lsl #16 dup v0.4s, w8mov w8, #52432 movk w8, #15820,lsl #16 dup v1.4s, w8 ldp q2, q3,[x1]fmul v1.4s, v3.4s, v1.4s ldr q3,[x0]fsub v2.4s, v3.4s, v2.4sfmul v0.4s, v2.4s, v0.4sfadd v0.4s, v0.4s, v1.4s stp q3, q0,[x1]str q0,[x2]ret

Temporal Mode

In temporal mode (--timesteps=T), SignalScript generates a function that runs a single instance of the original kernel over T sequential inputs, writing the output at each step to a buffer. It has the signature (float[T] input, float[S] state, float[T] output) -> ()

./build/ssc test/kernels.ss --fn=pipeline --timesteps=2 --avx512

This results in the following assembly, where:

  • input[2]: [rdi]
  • rate_of_change.prev: [rsi]
  • ema.prev: [rsi + 4]
  • output[2]: [rdx]

Note the loop unrolling.

pipeline_temporal:vmovssxmm0, dword ptr [rdi] vsubss xmm1,xmm0, dword ptr [rsi]vmovss dword ptr [rsi],xmm0vmovssxmm0, dword ptr [rip+ .LCPI0_0] vmulss xmm1,xmm1,xmm0vmovssxmm2, dword ptr [rip+ .LCPI0_1] vmulss xmm3,xmm2, dword ptr [rsi+4] vaddss xmm1,xmm1,xmm3vmovss dword ptr [rsi+4],xmm1vmovss dword ptr [rdx],xmm1vmovssxmm1, dword ptr [rdi+4] vsubss xmm3,xmm1, dword ptr [rsi]vmovss dword ptr [rsi],xmm1 vmulss xmm1,xmm2, dword ptr [rsi+4] vmulss xmm0,xmm3,xmm0 vaddss xmm0,xmm0,xmm1vmovss dword ptr [rsi+4],xmm0vmovss dword ptr [rdx+4],xmm0ret.LCPI0_0: .long 0x3f666666 #float0.899999976.LCPI0_1: .long 0x3dccccd0 #float0.100000024

Temporal Batched Model

In temporal batched mode (--timesteps=T --batch=B), SignalScript generates a function that runs B instances of the original kernel in parallel over T sequential inputs, writing the output at each step to a buffer. It has the signature (float[T] input, float[S,B] state, float[T,B] output) -> ()

State variables are again automatically converted to Structure-of-Arrays form (float[S,B] rather than float[B,S]) such that each individual state variable is contiguous in memory across all batches, allowing vectorization.

./build/ssc test/kernels.ss --fn=pipeline --batch=16 --timesteps=2 --avx512

This results in the following assembly, where:

  • input[2]: [rdi]
  • rate_of_change.prev[16]: [rsi]
  • ema.prev[16]: [rsi + 64]
  • output[2][16]: [rdx]

Notice the broadcasting of the scalar input, loop unrolling, and computation of all 16 instances of the kernel in parallel via the 512 bit zmm registers.

pipeline_temporal_batched:vbroadcastsszmm0, dword ptr [rdi] vsubps zmm1,zmm0, zmmword ptr [rsi]vmovups zmmword ptr [rsi],zmm0vbroadcastsszmm0, dword ptr [rip+ .LCPI0_0] vmulps zmm1,zmm1,zmm0vbroadcastsszmm2, dword ptr [rip+ .LCPI0_1] vmulps zmm3,zmm2, zmmword ptr [rsi+64] vaddps zmm1,zmm1,zmm3vmovups zmmword ptr [rsi+64],zmm1vmovups zmmword ptr [rdx],zmm1vbroadcastsszmm1, dword ptr [rdi+4] vsubps zmm3,zmm1, zmmword ptr [rsi]vmovups zmmword ptr [rsi],zmm1 vmulps zmm1,zmm2, zmmword ptr [rsi+64] vmulps zmm0,zmm3,zmm0 vaddps zmm0,zmm0,zmm1vmovups zmmword ptr [rsi+64],zmm0vmovups zmmword ptr [rdx+64],zmm0vzeroupperret.LCPI0_0: .long 0x3f666666 #float0.899999976.LCPI0_1: .long 0x3dccccd0 #float0.100000024

About

A compiler and DSL which uses MLIR to compile stateful signal processing kernels to vectorized assembly

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages