You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

209 lines
7.8 KiB

const bigInt=require("big-integer");
class ZqBuilder {
constructor(q, name) {
this.q=bigInt(q);
this.h = [];
this.c = [];
this.name = name;
}
build() {
this._buildHeaders();
this._buildAdd();
this._buildMul();
this.c.push(""); this.h.push("");
return [this.h.join("\n"), this.c.join("\n")];
}
_buildHeaders() {
this.n64 = Math.floor((this.q.bitLength() - 1) / 64)+1;
this.h.push("typedef unsigned long long u64;");
this.h.push(`typedef u64 ${this.name}Element[${this.n64}];`);
this.h.push(`typedef u64 *P${this.name}Element;`);
this.h.push(`extern ${this.name}Element ${this.name}_q;`);
this.h.push(`#define ${this.name}_N64 ${this.n64}`);
this.c.push(`#include "${this.name.toLowerCase()}.h"`);
this._defineConstant(`${this.name}_q`, this.q);
this.c.push(""); this.h.push("");
}
_defineConstant(n, v) {
let S = `${this.name}Element ${n}={`;
const mask = bigInt("FFFFFFFFFFFFFFFF", 16);
for (let i=0; i<this.n64; i++) {
if (i>0) S = S+",";
let shex = v.shiftRight(i*64).and(mask).toString(16);
while (shex <16) shex = "0" + shex;
S = S + "0x" + shex + "ULL";
}
S += "};";
this.c.push(S);
}
_buildAdd() {
this.h.push(`void ${this.name}_add(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b);`);
this.c.push(`void ${this.name}_add(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b) {`);
this.c.push(" __asm__ __volatile__ (");
for (let i=0; i<this.n64; i++) {
this.c.push(` "movq ${i*8}(%2), %%rax;"`);
this.c.push(` "${i==0 ? "addq" : "adcq"} ${i*8}(%1), %%rax;"`);
this.c.push(` "movq %%rax, ${i*8}(%0);"`);
}
this.c.push(" \"jc SQ;\"");
for (let i=0; i<this.n64; i++) {
if (i>0) {
this.c.push(` "movq ${(this.n64 - i-1)*8}(%0), %%rax;"`);
}
this.c.push(` "cmp ${(this.n64 - i-1)*8}(%3), %%rax;"`);
this.c.push(" \"jg SQ;\"");
this.c.push(" \"jl DONE;\"");
}
this.c.push(" \"SQ:\"");
for (let i=0; i<this.n64; i++) {
this.c.push(` "movq ${i*8}(%3), %%rax;"`);
this.c.push(` "${i==0 ? "subq" : "sbbq"} %%rax, ${i*8}(%0);"`);
}
this.c.push(" \"DONE:\"");
this.c.push(` :: "r" (r), "r" (a), "r" (b), "r" (${this.name}_q) : "%rax", "memory");`);
this.c.push("}\n");
}
_buildMul() {
let r0, r1, r2;
function setR(step) {
if ((step % 3) == 0) {
r0 = "%%r8";
r1 = "%%r9";
r2 = "%%r10";
} else if ((step % 3) == 1) {
r0 = "%%r9";
r1 = "%%r10";
r2 = "%%r8";
} else {
r0 = "%%r10";
r1 = "%%r8";
r2 = "%%r9";
}
}
const base = bigInt.one.shiftLeft(64);
const np64 = base.minus(this.q.modInv(base));
this.h.push(`void ${this.name}_mul(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b);`);
this.c.push(`void ${this.name}_mul(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b) {`);
this.c.push(" __asm__ __volatile__ (");
this.c.push(` "subq $${this.n64*8}, %%rsp;"`);
this.c.push(` "movq $0x${np64.toString(16)}, %%r11;"`);
this.c.push(" \"movq $0x0, %%r8;\"");
this.c.push(" \"movq $0x0, %%r9;\"");
this.c.push(" \"movq $0x0, %%r10;\"");
for (let i=0; i<this.n64*2; i++) {
setR(i);
for (let o1=Math.max(0, i-this.n64+1); (o1<=i)&&(o1<this.n64); o1++) {
const o2= i-o1;
this.c.push(` "movq ${o1*8}(%1), %%rax;"`);
this.c.push(` "mulq ${o2*8}(%2);"`);
this.c.push(` "addq %%rax, ${r0};"`);
this.c.push(` "adcq %%rdx, ${r1};"`);
this.c.push(` "adcq $0x0, ${r2};"`);
}
for (let j=i-1; j>=0; j--) {
if (((i-j)<this.n64)&&(j<this.n64)) {
this.c.push(` "movq ${j*8}(%%rsp), %%rax;"`);
this.c.push(` "mulq ${(i-j)*8}(%3);"`);
this.c.push(` "addq %%rax, ${r0};"`);
this.c.push(` "adcq %%rdx, ${r1};"`);
this.c.push(` "adcq $0x0, ${r2};"`);
}
}
if (i<this.n64) {
this.c.push(` "movq ${r0}, %%rax;"`);
this.c.push(" \"mulq %%r11;\"");
this.c.push(` "movq %%rax, ${i*8}(%%rsp);"`);
this.c.push(" \"mulq (%3);\"");
this.c.push(` "addq %%rax, ${r0};"`);
this.c.push(` "adcq %%rdx, ${r1};"`);
this.c.push(` "adcq $0x0, ${r2};"`);
} else {
this.c.push(` "movq ${r0}, ${(i-this.n64)*8}(%0);"`);
this.c.push(` "movq $0, ${r0};"`);
}
}
this.c.push(` "cmp $0, ${r1};"`);
this.c.push(" \"jne SQ2;\"");
for (let i=0; i<this.n64; i++) {
this.c.push(` "movq ${(this.n64 - i-1)*8}(%0), %%rax;"`);
this.c.push(` "cmp ${(this.n64 - i-1)*8}(%3), %%rax;"`);
this.c.push(" \"jg SQ2;\"");
this.c.push(" \"jl DONE2;\"");
}
this.c.push(" \"SQ2:\"");
for (let i=0; i<this.n64; i++) {
this.c.push(` "movq ${i*8}(%3), %%rax;"`);
this.c.push(` "${i==0 ? "subq" : "sbbq"} %%rax, ${i*8}(%0);"`);
}
this.c.push(" \"DONE2:\"");
this.c.push(` "addq $${this.n64*8}, %%rsp;"`);
this.c.push(` :: "r" (r), "r" (a), "r" (b), "r" (${this.name}_q) : "%rax", "%rdx", "%r8", "%r9", "%r10", "%r11", "memory");`);
this.c.push("}\n");
}
_buildIDiv() {
this.h.push(`void ${this.name}_idiv(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b);`);
this.c.push(`void ${this.name}_idiv(P${this.name}Element r, P${this.name}Element a, P${this.name}Element b) {`);
this.c.push(" __asm__ __volatile__ (");
this.c.push(" \"pxor %%xmm0, %%xmm0;\""); // Comparison Register
if (this.n64 == 1) {
this.c.push(` "mov %%rax, $${this.n64 - 8};"`);
} else {
this.c.push(` "mov %%rax, $${this.n64 -16};"`);
}
this.c.push(` :: "r" (r), "r" (a), "r" (b), "r" (${this.name}_q) : "%rax", "%rdx", "%r8", "%r9", "%r10", "%r11", "memory");`);
this.c.push("}\n");
}
}
var runningAsScript = !module.parent;
if (runningAsScript) {
const fs = require("fs");
var argv = require("yargs")
.usage("Usage: $0 -q [primeNum] -n [name] -oc [out .c file] -oh [out .h file]")
.demandOption(["q","n"])
.alias("q", "prime")
.alias("n", "name")
.argv;
const q = bigInt(argv.q);
const cFileName = (argv.oc) ? argv.oc : argv.name.toLowerCase() + ".c";
const hFileName = (argv.oh) ? argv.oh : argv.name.toLowerCase() + ".h";
const builder = new ZqBuilder(q, argv.name);
const res = builder.build();
fs.writeFileSync(hFileName, res[0], "utf8");
fs.writeFileSync(cFileName, res[1], "utf8");
} else {
module.exports = function(q, name) {
const builder = new ZqBuilder(q, name);
return builder.build();
};
}