UNPKG

wasmcurves

Version:

elliptic curves implementations in wasm

1,367 lines (1,186 loc) 41 kB
/* Copyright 2019 0KIMS association. This file is part of wasmsnark (Web Assembly zkSnark Prover). wasmsnark is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. wasmsnark is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with wasmsnark. If not, see <https://www.gnu.org/licenses/>. */ const { isOdd, modInv, modPow } = require("./bigint.js"); const utils = require("./utils.js"); module.exports = function buildFFT(module, prefix, gPrefix, fPrefix, opGtimesF) { const n64f = module.modules[fPrefix].n64; const n8f = n64f*8; const n64g = module.modules[gPrefix].n64; const n8g = n64g*8; const q = module.modules[fPrefix].q; let rem = q - 1n; let maxBits = 0; while (!isOdd(rem)) { maxBits ++; rem = rem >> 1n; } let nr = 2n; while ( modPow(nr, q >> 1n, q) === 1n ) nr = nr + 1n; // console.log(nr); const w = new Array(maxBits+1); w[maxBits] = modPow(nr, rem, q); let n=maxBits-1; while (n>=0) { w[n] = modPow(w[n+1], 2n, q); n--; } const bytes = []; const R = (1n << BigInt(n8f*8)) % q; for (let i=0; i<w.length; i++) { const m = w[i] * R % q; bytes.push(...utils.bigInt2BytesLE(m, n8f)); } const ROOTs = module.alloc(bytes); const i2 = new Array(maxBits+1); i2[0] = 1n; for (let i=1; i<=maxBits; i++) { i2[i] = i2[i-1] * 2n; } const bytesi2 =[]; for (let i=0; i<=maxBits; i++) { const m = modInv(i2[i], q) * R % q; bytesi2.push(...utils.bigInt2BytesLE(m, n8f)); } const INV2 = module.alloc(bytesi2); const shift = modPow(nr, 2n, q); const bytesShiftToSmallM =[]; const bytesSConst =[]; for (let i=0; i<=maxBits; i++) { const shiftToSmallM = modPow(shift, 2n ** BigInt(i), q); const sConst = modInv(q + 1n - shiftToSmallM, q); bytesShiftToSmallM.push(...utils.bigInt2BytesLE(shiftToSmallM * R % q, n8f)); bytesSConst.push(...utils.bigInt2BytesLE(sConst * R % q, n8f)); } const SHIFT_TO_M = module.alloc( bytesShiftToSmallM ); const SCONST = module.alloc( bytesSConst ); function rev(x) { let r=0; for (let i=0; i<8; i++) { if (x & (1 << i)) { r = r | (0x80 >> i); } } return r; } const rtable = Array(256); for (let i=0; i<256; i++) { rtable[i] = rev(i); } const REVTABLE = module.alloc(rtable); function buildLog2() { const f = module.addFunction(prefix+"__log2"); f.addParam("n", "i32"); f.setReturnType("i32"); f.addLocal("bits", "i32"); f.addLocal("aux", "i32"); const c = f.getCodeBuilder(); f.addCode( c.setLocal( "aux", c.i32_shr_u( c.getLocal("n"), c.i32_const(1) ) ) ); f.addCode(c.setLocal("bits", c.i32_const(0))); f.addCode(c.block(c.loop( c.br_if( 1, c.i32_eqz(c.getLocal("aux")) ), c.setLocal( "aux", c.i32_shr_u( c.getLocal("aux"), c.i32_const(1) ) ), c.setLocal( "bits", c.i32_add( c.getLocal("bits"), c.i32_const(1) ) ), c.br(0) ))); f.addCode(c.if( c.i32_ne( c.getLocal("n"), c.i32_shl( c.i32_const(1), c.getLocal("bits") ) ), c.unreachable() )); f.addCode(c.if( c.i32_gt_u( c.getLocal("bits"), c.i32_const(maxBits) ), c.unreachable() )); f.addCode(c.getLocal("bits")); } function buildFFT() { const f = module.addFunction(prefix+"_fft"); f.addParam("px", "i32"); f.addParam("n", "i32"); f.addLocal("bits", "i32"); const c = f.getCodeBuilder(); const One = c.i32_const(module.alloc(n8f)); f.addCode( c.setLocal( "bits", c.call( prefix + "__log2", c.getLocal("n") ) ), c.call(fPrefix + "_one", One), c.call( prefix+"_rawfft", c.getLocal("px"), c.getLocal("bits"), c.i32_const(0), One ) ); } function buildIFFT() { const f = module.addFunction(prefix+"_ifft"); f.addParam("px", "i32"); f.addParam("n", "i32"); f.addLocal("bits", "i32"); f.addLocal("pInv2", "i32"); const c = f.getCodeBuilder(); f.addCode( c.setLocal( "bits", c.call( prefix + "__log2", c.getLocal("n") ) ), c.setLocal( "pInv2", c.i32_add( c.i32_const(INV2), c.i32_mul( c.getLocal("bits"), c.i32_const(n8f) ) ) ), c.call( prefix+"_rawfft", c.getLocal("px"), c.getLocal("bits"), c.i32_const(1), c.getLocal("pInv2") ), ); } function buildRawFFT() { const f = module.addFunction(prefix+"_rawfft"); f.addParam("px", "i32"); f.addParam("bits", "i32"); // 2 power f.addParam("reverse", "i32"); f.addParam("mulFactor", "i32"); f.addLocal("s", "i32"); f.addLocal("k", "i32"); f.addLocal("j", "i32"); f.addLocal("m", "i32"); f.addLocal("mdiv2", "i32"); f.addLocal("n", "i32"); f.addLocal("pwm", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); const c = f.getCodeBuilder(); const W = c.i32_const(module.alloc(n8f)); const T = c.i32_const(module.alloc(n8g)); const U = c.i32_const(module.alloc(n8g)); f.addCode( c.call(prefix + "__reversePermutation", c.getLocal("px"), c.getLocal("bits")), c.setLocal("n", c.i32_shl(c.i32_const(1), c.getLocal("bits"))), c.setLocal("s", c.i32_const(1)), c.block(c.loop( c.br_if( 1, c.i32_gt_u( c.getLocal("s"), c.getLocal("bits") ) ), c.setLocal("m", c.i32_shl(c.i32_const(1), c.getLocal("s"))), c.setLocal("pwm", c.i32_add( c.i32_const(ROOTs), c.i32_mul( c.getLocal("s"), c.i32_const(n8f) ) ) ), c.setLocal("k", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_ge_u( c.getLocal("k"), c.getLocal("n") ) ), c.call(fPrefix + "_one", W), c.setLocal("mdiv2", c.i32_shr_u(c.getLocal("m"), c.i32_const(1)) ), c.setLocal("j", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_ge_u( c.getLocal("j"), c.getLocal("mdiv2") ) ), c.setLocal( "idx1", c.i32_add( c.getLocal("px"), c.i32_mul( c.i32_add( c.getLocal("k"), c.getLocal("j") ), c.i32_const(n8g) ) ) ), c.setLocal( "idx2", c.i32_add( c.getLocal("idx1"), c.i32_mul( c.getLocal("mdiv2"), c.i32_const(n8g) ) ) ), c.call( opGtimesF, c.getLocal("idx2"), W, T ), c.call( gPrefix + "_copy", c.getLocal("idx1"), U ), c.call( gPrefix + "_add", U, T, c.getLocal("idx1"), ), c.call( gPrefix + "_sub", U, T, c.getLocal("idx2"), ), c.call( fPrefix + "_mul", W, c.getLocal("pwm"), W, ), c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), c.br(0) )), c.setLocal("k", c.i32_add(c.getLocal("k"), c.getLocal("m"))), c.br(0) )), c.setLocal("s", c.i32_add(c.getLocal("s"), c.i32_const(1))), c.br(0) )), c.call( prefix + "__fftFinal", c.getLocal("px"), c.getLocal("bits"), c.getLocal("reverse"), c.getLocal("mulFactor") ) ); } function buildFinalInverse() { const f = module.addFunction(prefix+"__fftFinal"); f.addParam("px", "i32"); f.addParam("bits", "i32"); f.addParam("reverse", "i32"); f.addParam("mulFactor", "i32"); f.addLocal("n", "i32"); f.addLocal("ndiv2", "i32"); f.addLocal("pInv2", "i32"); f.addLocal("i", "i32"); f.addLocal("mask", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); const c = f.getCodeBuilder(); const T = c.i32_const(module.alloc(n8g)); f.addCode( c.if( c.i32_and( c.i32_eqz(c.getLocal("reverse")), c.call(fPrefix + "_isOne", c.getLocal("mulFactor")) ), c.ret([]) ), c.setLocal("n", c.i32_shl( c.i32_const(1), c.getLocal("bits"))), c.setLocal("mask", c.i32_sub( c.getLocal("n") , c.i32_const(1))), c.setLocal("i", c.i32_const(1)), c.setLocal( "ndiv2", c.i32_shr_u( c.getLocal("n"), c.i32_const(1) ) ), c.block(c.loop( c.br_if( 1, c.i32_ge_u( c.getLocal("i"), c.getLocal("ndiv2") ) ), c.setLocal("idx1", c.i32_add( c.getLocal("px"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.setLocal("idx2", c.i32_add( c.getLocal("px"), c.i32_mul( c.i32_sub( c.getLocal("n"), c.getLocal("i") ), c.i32_const(n8g) ) ) ), c.if( c.getLocal("reverse"), c.if( c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), [ ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), ...c.call(gPrefix + "_copy", c.getLocal("idx2") , c.getLocal("idx1") ), ...c.call(gPrefix + "_copy", T , c.getLocal("idx2")), ], [ ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), ...c.call(opGtimesF , c.getLocal("idx2") , c.getLocal("mulFactor"), c.getLocal("idx1") ), ...c.call(opGtimesF , T , c.getLocal("mulFactor"), c.getLocal("idx2")), ] ), c.if( c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), [ // Do nothing (It should not be here) ], [ ...c.call(opGtimesF , c.getLocal("idx1") , c.getLocal("mulFactor"), c.getLocal("idx1") ), ...c.call(opGtimesF , c.getLocal("idx2") , c.getLocal("mulFactor"), c.getLocal("idx2")), ] ) ), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )), c.if( c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), [ // Do nothing (It should not be here) ], [ ...c.call(opGtimesF, c.getLocal("px") , c.getLocal("mulFactor"), c.getLocal("px")), ...c.setLocal("idx2", c.i32_add( c.getLocal("px"), c.i32_mul( c.getLocal("ndiv2"), c.i32_const(n8g) ) ) ), ...c.call(opGtimesF, c.getLocal("idx2"),c.getLocal("mulFactor"), c.getLocal("idx2")) ] ) ); } function buildReversePermutation() { const f = module.addFunction(prefix+"__reversePermutation"); f.addParam("px", "i32"); f.addParam("bits", "i32"); f.addLocal("n", "i32"); f.addLocal("i", "i32"); f.addLocal("ri", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); const c = f.getCodeBuilder(); const T = c.i32_const(module.alloc(n8g)); f.addCode( c.setLocal("n", c.i32_shl( c.i32_const(1), c.getLocal("bits"))), c.setLocal("i", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_eq( c.getLocal("i"), c.getLocal("n") ) ), c.setLocal("idx1", c.i32_add( c.getLocal("px"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.setLocal("ri", c.call(prefix + "__rev", c.getLocal("i"), c.getLocal("bits"))), c.setLocal("idx2", c.i32_add( c.getLocal("px"), c.i32_mul( c.getLocal("ri"), c.i32_const(n8g) ) ) ), c.if( c.i32_lt_u( c.getLocal("i"), c.getLocal("ri") ), [ ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), ...c.call(gPrefix + "_copy", c.getLocal("idx2") , c.getLocal("idx1")), ...c.call(gPrefix + "_copy", T , c.getLocal("idx2")) ] ), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } function buildRev() { const f = module.addFunction(prefix+"__rev"); f.addParam("x", "i32"); f.addParam("bits", "i32"); f.setReturnType("i32"); const c = f.getCodeBuilder(); f.addCode( c.i32_rotl( c.i32_add( c.i32_add( c.i32_shl( c.i32_load8_u( c.i32_and( c.getLocal("x"), c.i32_const(0xFF) ), REVTABLE, 0 ), c.i32_const(24) ), c.i32_shl( c.i32_load8_u( c.i32_and( c.i32_shr_u( c.getLocal("x"), c.i32_const(8) ), c.i32_const(0xFF) ), REVTABLE, 0 ), c.i32_const(16) ), ), c.i32_add( c.i32_shl( c.i32_load8_u( c.i32_and( c.i32_shr_u( c.getLocal("x"), c.i32_const(16) ), c.i32_const(0xFF) ), REVTABLE, 0 ), c.i32_const(8) ), c.i32_load8_u( c.i32_and( c.i32_shr_u( c.getLocal("x"), c.i32_const(24) ), c.i32_const(0xFF) ), REVTABLE, 0 ), ) ), c.getLocal("bits") ) ); } function buildFFTJoin() { const f = module.addFunction(prefix+"_fftJoin"); f.addParam("pBuff1", "i32"); f.addParam("pBuff2", "i32"); f.addParam("n", "i32"); f.addParam("first", "i32"); f.addParam("inc", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); f.addLocal("i", "i32"); const c = f.getCodeBuilder(); const W = c.i32_const(module.alloc(n8f)); const T = c.i32_const(module.alloc(n8g)); const U = c.i32_const(module.alloc(n8g)); f.addCode( c.call( fPrefix + "_copy", c.getLocal("first"), W), c.setLocal("i", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_eq( c.getLocal("i"), c.getLocal("n") ) ), c.setLocal( "idx1", c.i32_add( c.getLocal("pBuff1"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.setLocal( "idx2", c.i32_add( c.getLocal("pBuff2"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.call( opGtimesF, c.getLocal("idx2"), W, T ), c.call( gPrefix + "_copy", c.getLocal("idx1"), U ), c.call( gPrefix + "_add", U, T, c.getLocal("idx1"), ), c.call( gPrefix + "_sub", U, T, c.getLocal("idx2"), ), c.call( fPrefix + "_mul", W, c.getLocal("inc"), W, ), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } function buildFFTJoinExt() { const f = module.addFunction(prefix+"_fftJoinExt"); f.addParam("pBuff1", "i32"); f.addParam("pBuff2", "i32"); f.addParam("n", "i32"); f.addParam("first", "i32"); f.addParam("inc", "i32"); f.addParam("totalBits", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); f.addLocal("i", "i32"); f.addLocal("pShiftToM", "i32"); const c = f.getCodeBuilder(); const W = c.i32_const(module.alloc(n8f)); const U = c.i32_const(module.alloc(n8g)); f.addCode( c.setLocal("pShiftToM", c.i32_add( c.i32_const(SHIFT_TO_M), c.i32_mul( c.getLocal("totalBits"), c.i32_const(n8f) ) ) ), c.call( fPrefix + "_copy", c.getLocal("first"), W), c.setLocal("i", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_eq( c.getLocal("i"), c.getLocal("n") ) ), c.setLocal( "idx1", c.i32_add( c.getLocal("pBuff1"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.setLocal( "idx2", c.i32_add( c.getLocal("pBuff2"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.call( gPrefix + "_add", c.getLocal("idx1"), c.getLocal("idx2"), U ), c.call( opGtimesF, c.getLocal("idx2"), c.getLocal("pShiftToM"), c.getLocal("idx2") ), c.call( gPrefix + "_add", c.getLocal("idx1"), c.getLocal("idx2"), c.getLocal("idx2") ), c.call( opGtimesF, c.getLocal("idx2"), W, c.getLocal("idx2"), ), c.call( gPrefix + "_copy", U, c.getLocal("idx1") ), c.call( fPrefix + "_mul", W, c.getLocal("inc"), W ), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } function buildFFTJoinExtInv() { const f = module.addFunction(prefix+"_fftJoinExtInv"); f.addParam("pBuff1", "i32"); f.addParam("pBuff2", "i32"); f.addParam("n", "i32"); f.addParam("first", "i32"); f.addParam("inc", "i32"); f.addParam("totalBits", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); f.addLocal("i", "i32"); f.addLocal("pShiftToM", "i32"); f.addLocal("pSConst", "i32"); const c = f.getCodeBuilder(); const W = c.i32_const(module.alloc(n8f)); const U = c.i32_const(module.alloc(n8g)); f.addCode( c.setLocal("pShiftToM", c.i32_add( c.i32_const(SHIFT_TO_M), c.i32_mul( c.getLocal("totalBits"), c.i32_const(n8f) ) ) ), c.setLocal("pSConst", c.i32_add( c.i32_const(SCONST), c.i32_mul( c.getLocal("totalBits"), c.i32_const(n8f) ) ) ), c.call( fPrefix + "_copy", c.getLocal("first"), W), c.setLocal("i", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_eq( c.getLocal("i"), c.getLocal("n") ) ), c.setLocal( "idx1", c.i32_add( c.getLocal("pBuff1"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.setLocal( "idx2", c.i32_add( c.getLocal("pBuff2"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.call( opGtimesF, c.getLocal("idx2"), W, U ), c.call( gPrefix + "_sub", c.getLocal("idx1"), U, c.getLocal("idx2"), ), c.call( opGtimesF, c.getLocal("idx2"), c.getLocal("pSConst"), c.getLocal("idx2") ), c.call( opGtimesF, c.getLocal("idx1"), c.getLocal("pShiftToM"), c.getLocal("idx1") ), c.call( gPrefix + "_sub", U, c.getLocal("idx1"), c.getLocal("idx1") ), c.call( opGtimesF, c.getLocal("idx1"), c.getLocal("pSConst"), c.getLocal("idx1") ), c.call( fPrefix + "_mul", W, c.getLocal("inc"), W ), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } function buildPrepareLagrangeEvaluation() { const f = module.addFunction(prefix+"_prepareLagrangeEvaluation"); f.addParam("pBuff1", "i32"); f.addParam("pBuff2", "i32"); f.addParam("n", "i32"); f.addParam("first", "i32"); f.addParam("inc", "i32"); f.addParam("totalBits", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); f.addLocal("i", "i32"); f.addLocal("pShiftToM", "i32"); f.addLocal("pSConst", "i32"); const c = f.getCodeBuilder(); const W = c.i32_const(module.alloc(n8f)); const U = c.i32_const(module.alloc(n8g)); f.addCode( c.setLocal("pShiftToM", c.i32_add( c.i32_const(SHIFT_TO_M), c.i32_mul( c.getLocal("totalBits"), c.i32_const(n8f) ) ) ), c.setLocal("pSConst", c.i32_add( c.i32_const(SCONST), c.i32_mul( c.getLocal("totalBits"), c.i32_const(n8f) ) ) ), c.call( fPrefix + "_copy", c.getLocal("first"), W), c.setLocal("i", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_eq( c.getLocal("i"), c.getLocal("n") ) ), c.setLocal( "idx1", c.i32_add( c.getLocal("pBuff1"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.setLocal( "idx2", c.i32_add( c.getLocal("pBuff2"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.call( opGtimesF, c.getLocal("idx1"), c.getLocal("pShiftToM"), U ), c.call( gPrefix + "_sub", c.getLocal("idx2"), U, U ), c.call( gPrefix + "_sub", c.getLocal("idx1"), c.getLocal("idx2"), c.getLocal("idx2"), ), c.call( opGtimesF, U, c.getLocal("pSConst"), c.getLocal("idx1"), ), c.call( opGtimesF, c.getLocal("idx2"), W, c.getLocal("idx2"), ), c.call( fPrefix + "_mul", W, c.getLocal("inc"), W ), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } function buildFFTMix() { const f = module.addFunction(prefix+"_fftMix"); f.addParam("pBuff", "i32"); f.addParam("n", "i32"); f.addParam("exp", "i32"); f.addLocal("nGroups", "i32"); f.addLocal("nPerGroup", "i32"); f.addLocal("nPerGroupDiv2", "i32"); f.addLocal("pairOffset", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); f.addLocal("i", "i32"); f.addLocal("j", "i32"); f.addLocal("pwm", "i32"); const c = f.getCodeBuilder(); const W = c.i32_const(module.alloc(n8f)); const T = c.i32_const(module.alloc(n8g)); const U = c.i32_const(module.alloc(n8g)); f.addCode( c.setLocal("nPerGroup", c.i32_shl(c.i32_const(1), c.getLocal("exp"))), c.setLocal("nPerGroupDiv2", c.i32_shr_u(c.getLocal("nPerGroup"), c.i32_const(1))), c.setLocal("nGroups", c.i32_shr_u(c.getLocal("n"), c.getLocal("exp"))), c.setLocal("pairOffset", c.i32_mul(c.getLocal("nPerGroupDiv2"), c.i32_const(n8g))), c.setLocal("pwm", c.i32_add( c.i32_const(ROOTs), c.i32_mul( c.getLocal("exp"), c.i32_const(n8f) ) ) ), c.setLocal("i", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_eq( c.getLocal("i"), c.getLocal("nGroups") ) ), c.call( fPrefix + "_one", W), c.setLocal("j", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_eq( c.getLocal("j"), c.getLocal("nPerGroupDiv2") ) ), c.setLocal( "idx1", c.i32_add( c.getLocal("pBuff"), c.i32_mul( c.i32_add( c.i32_mul( c.getLocal("i"), c.getLocal("nPerGroup") ), c.getLocal("j") ), c.i32_const(n8g) ) ) ), c.setLocal( "idx2", c.i32_add( c.getLocal("idx1"), c.getLocal("pairOffset") ) ), c.call( opGtimesF, c.getLocal("idx2"), W, T ), c.call( gPrefix + "_copy", c.getLocal("idx1"), U ), c.call( gPrefix + "_add", U, T, c.getLocal("idx1"), ), c.call( gPrefix + "_sub", U, T, c.getLocal("idx2"), ), c.call( fPrefix + "_mul", W, c.getLocal("pwm"), W, ), c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), c.br(0) )), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } // Reverse all and multiply by factor function buildFFTFinal() { const f = module.addFunction(prefix+"_fftFinal"); f.addParam("pBuff", "i32"); f.addParam("n", "i32"); f.addParam("factor", "i32"); f.addLocal("idx1", "i32"); f.addLocal("idx2", "i32"); f.addLocal("i", "i32"); f.addLocal("ndiv2", "i32"); const c = f.getCodeBuilder(); const T = c.i32_const(module.alloc(n8g)); f.addCode( c.setLocal("ndiv2", c.i32_shr_u(c.getLocal("n"), c.i32_const(1))), c.if( c.i32_and( c.getLocal("n"), c.i32_const(1) ), c.call( opGtimesF, c.i32_add( c.getLocal("pBuff"), c.i32_mul( c.getLocal("ndiv2"), c.i32_const(n8g) ) ), c.getLocal("factor"), c.i32_add( c.getLocal("pBuff"), c.i32_mul( c.getLocal("ndiv2"), c.i32_const(n8g) ) ), ), ), c.setLocal("i", c.i32_const(0)), c.block(c.loop( c.br_if( 1, c.i32_ge_u( c.getLocal("i"), c.getLocal("ndiv2") ) ), c.setLocal( "idx1", c.i32_add( c.getLocal("pBuff"), c.i32_mul( c.getLocal("i"), c.i32_const(n8g) ) ) ), c.setLocal( "idx2", c.i32_add( c.getLocal("pBuff"), c.i32_mul( c.i32_sub( c.i32_sub( c.getLocal("n"), c.i32_const(1) ), c.getLocal("i") ), c.i32_const(n8g) ) ) ), c.call( opGtimesF, c.getLocal("idx2"), c.getLocal("factor"), T ), c.call( opGtimesF, c.getLocal("idx1"), c.getLocal("factor"), c.getLocal("idx2"), ), c.call( gPrefix + "_copy", T, c.getLocal("idx1"), ), c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } buildRev(); buildReversePermutation(); buildFinalInverse(); buildRawFFT(); buildLog2(); buildFFT(); buildIFFT(); buildFFTJoin(); buildFFTJoinExt(); buildFFTJoinExtInv(); buildFFTMix(); buildFFTFinal(); buildPrepareLagrangeEvaluation(); module.exportFunction(prefix+"_fft"); module.exportFunction(prefix+"_ifft"); module.exportFunction(prefix+"_rawfft"); module.exportFunction(prefix+"_fftJoin"); module.exportFunction(prefix+"_fftJoinExt"); module.exportFunction(prefix+"_fftJoinExtInv"); module.exportFunction(prefix+"_fftMix"); module.exportFunction(prefix+"_fftFinal"); module.exportFunction(prefix+"_prepareLagrangeEvaluation"); };