const assert = require("assert");
|
|
const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
|
|
const ModuleBuilderWat = require("wasmbuilder").ModuleBuilderWat;
|
|
const buildRuntime = require("./build_runtime");
|
|
const Scalar = require("ffjavascript").Scalar;
|
|
const F1Field = require("ffjavascript").F1Field;
|
|
|
|
|
|
const errs = require("./errs");
|
|
|
|
function hexToBytesR(hex) {
|
|
for (var bytes = [], c = hex.length-2; c >=0; c -= 2)
|
|
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
return bytes;
|
|
}
|
|
|
|
|
|
function intToBytes32(v) {
|
|
return [ v &0xFF, (v>>8) & 0xFF, (v>>16) & 0xFF, (v>>24) & 0xFF ];
|
|
}
|
|
|
|
|
|
class CodeBuilderWasm {
|
|
constructor(fnBuilder) {
|
|
this.fnBuilder = fnBuilder;
|
|
this.ops = [];
|
|
}
|
|
|
|
addComment(comment) {
|
|
this.ops.push({op: "COMMENT", comment});
|
|
}
|
|
|
|
addBlock(block) {
|
|
this.ops.push({op: "BLOCK", block});
|
|
}
|
|
|
|
calcOffset(dLabel, offsets) {
|
|
this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
|
|
}
|
|
|
|
assign(dLabel, src, sOffset) {
|
|
this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
|
|
}
|
|
|
|
getSubComponentOffset(dLabel, component, hash, hashLabel) {
|
|
this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
|
|
}
|
|
|
|
getSubComponentSizes(dLabel, component, hash, hashLabel) {
|
|
this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
|
|
}
|
|
|
|
getSignalOffset(dLabel, component, hash, hashLabel) {
|
|
this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
|
|
}
|
|
|
|
getSignalSizes(dLabel, component, hash, hashLabel) {
|
|
this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
|
|
}
|
|
|
|
setSignal(component, signal, value) {
|
|
this.ops.push({op: "SETSIGNAL", component, signal, value});
|
|
}
|
|
|
|
getSignal(dLabel, component, signal, n) {
|
|
if (typeof n == "undefined") n=1;
|
|
this.ops.push({op: "GETSIGNAL", dLabel, component, signal, n});
|
|
}
|
|
|
|
copyN(dLabel, offset, src, n) {
|
|
this.ops.push({op: "COPYN", dLabel, offset, src, n});
|
|
}
|
|
|
|
copyNRet(src, n) {
|
|
this.ops.push({op: "COPYNRET", src, n});
|
|
}
|
|
|
|
fieldOp(dLabel, fOp, params) {
|
|
this.ops.push({op: "FOP", dLabel, fOp, params});
|
|
}
|
|
|
|
ret() {
|
|
this.ops.push({op: "RET"});
|
|
}
|
|
|
|
addLoop(condLabel, body) {
|
|
this.ops.push({op: "LOOP", condLabel, body});
|
|
}
|
|
|
|
addIf(condLabel, thenCode, elseCode) {
|
|
this.ops.push({op: "IF", condLabel, thenCode, elseCode});
|
|
}
|
|
|
|
fnCall(fnName, retLabel, params) {
|
|
this.ops.push({op: "FNCALL", fnName, retLabel, params});
|
|
}
|
|
|
|
checkConstraint(a, b, strErr) {
|
|
this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
|
|
}
|
|
|
|
checkAssert(a, strErr) {
|
|
this.ops.push({op: "CHECKASSERT", a, strErr});
|
|
}
|
|
|
|
concat(cb) {
|
|
this.ops.push(...cb.ops);
|
|
}
|
|
|
|
log(val) {
|
|
this.ops.push({op: "LOG", val});
|
|
}
|
|
|
|
hasCode() {
|
|
for (let i=0; i<this.ops.length; i++) {
|
|
if (this.ops[i].op != "COMMENT") return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
_buildOffset(c, offsets) {
|
|
let rN=0;
|
|
let S = null;
|
|
offsets.forEach((o) => {
|
|
if ((o[0][0] == "V") && (o[1][0]== "V")) {
|
|
rN += o[0][1]*o[1][1];
|
|
return;
|
|
}
|
|
let f=[];
|
|
if (o[0][0] == "V") {
|
|
f = c.i32_const(o[0][1]);
|
|
} else if (o[0][0] == "RI") {
|
|
f = c.i32_load(this.fnBuilder._getPtr(c, o[0][1]));
|
|
} else if (o[0][0] == "R") {
|
|
f = c.call("Fr_toInt", this.fnBuilder._getPtr(c, o[0][1]));
|
|
} else {
|
|
assert(false);
|
|
}
|
|
if (o[1][0] == "V") {
|
|
if (o[1][1]==0) return;
|
|
if (o[1][1]>1) {
|
|
f = c.i32_mul(f, c.i32_const(o[1][1]));
|
|
}
|
|
} else if (o[1][0] == "RS") {
|
|
f = c.i32_mul(
|
|
f,
|
|
c.i32_load(
|
|
c.i32_add(
|
|
c.i32_load(this.fnBuilder._getPtr(c, o[1][1])),
|
|
c.i32_const(o[1][2]*4)
|
|
)
|
|
)
|
|
);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
if (S!=null) {
|
|
S = c.i32_add( S, f);
|
|
} else {
|
|
S = f;
|
|
}
|
|
});
|
|
if (rN>0) {
|
|
if (S!=null) {
|
|
S = c.i32_add( S, c.i32_const(rN));
|
|
} else {
|
|
S = c.i32_const(rN);
|
|
}
|
|
}
|
|
return S;
|
|
}
|
|
|
|
build(c) {
|
|
const code = [];
|
|
this.ops.forEach( (o) => {
|
|
if (o.op == "COMMENT") {
|
|
code.push(
|
|
c.comment(o.comment)
|
|
);
|
|
// DO nothing
|
|
} else if (o.op == "BLOCK") {
|
|
code.push(
|
|
o.block.build(c)
|
|
);
|
|
} else if (o.op == "CALCOFFSETS") {
|
|
code.push(
|
|
c.i32_store(
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
this._buildOffset(c, o.offsets)
|
|
)
|
|
);
|
|
} else if (o.op == "ASSIGN") {
|
|
code.push(
|
|
c.i32_store(
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
c.i32_add(
|
|
this.fnBuilder._deRefFr(c, o.src),
|
|
c.i32_mul(
|
|
this.fnBuilder._deRefInt(c, o.sOffset),
|
|
c.i32_const(this.fnBuilder.builder.sizeFr)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
} else if (o.op == "GETSUBCOMPONENTOFFSET") {
|
|
code.push(
|
|
c.call(
|
|
"getSubComponentOffset",
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
this.fnBuilder._deRefInt(c, o.component),
|
|
c.i64_const("0x" + o.hash)
|
|
)
|
|
);
|
|
} else if (o.op == "GETSUBCOMPONENTSIZES") {
|
|
code.push(
|
|
c.call(
|
|
"getSubComponentSizes",
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
this.fnBuilder._deRefInt(c, o.component),
|
|
c.i64_const("0x" + o.hash)
|
|
)
|
|
);
|
|
} else if (o.op == "GETSIGNALOFFSET") {
|
|
code.push(
|
|
c.call(
|
|
"getSignalOffset",
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
this.fnBuilder._deRefInt(c, o.component),
|
|
c.i64_const("0x" + o.hash)
|
|
)
|
|
);
|
|
} else if (o.op == "GETSIGNALSIZES") {
|
|
code.push(
|
|
c.call(
|
|
"getSignalSizes",
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
this.fnBuilder._deRefInt(c, o.component),
|
|
c.i64_const("0x" + o.hash)
|
|
)
|
|
);
|
|
} else if (o.op == "SETSIGNAL") {
|
|
code.push(
|
|
c.call(
|
|
"setSignal",
|
|
c.getLocal("cIdx"),
|
|
this.fnBuilder._deRefInt(c, o.component),
|
|
this.fnBuilder._deRefInt(c, o.signal),
|
|
this.fnBuilder._deRefFr(c, o.value)
|
|
)
|
|
);
|
|
} else if (o.op == "GETSIGNAL") {
|
|
code.push(
|
|
c.call(
|
|
"multiGetSignal",
|
|
c.getLocal("cIdx"),
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
this.fnBuilder._deRefInt(c, o.component),
|
|
this.fnBuilder._deRefInt(c, o.signal),
|
|
c.i32_const(o.n)
|
|
)
|
|
);
|
|
} else if (o.op == "COPYN") {
|
|
code.push(
|
|
c.call(
|
|
"Fr_copyn",
|
|
c.i32_add(
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
c.i32_mul(
|
|
this.fnBuilder._deRefInt(c, o.offset),
|
|
c.i32_const(this.fnBuilder.builder.sizeFr)
|
|
)
|
|
),
|
|
this.fnBuilder._deRefFr(c, o.src),
|
|
c.i32_const(o.n)
|
|
)
|
|
);
|
|
} else if (o.op == "COPYNRET") {
|
|
code.push(
|
|
c.call(
|
|
"Fr_copyn",
|
|
c.getLocal("pRet"),
|
|
this.fnBuilder._deRefFr(c, o.src),
|
|
c.i32_const(o.n)
|
|
)
|
|
);
|
|
} else if (o.op == "RET") {
|
|
code.push(this.fnBuilder._freeStack(c));
|
|
code.push(c.ret([]));
|
|
} else if (o.op == "FOP") {
|
|
let params = [];
|
|
for (let i=0; i<o.params.length; i++) {
|
|
params.push( this.fnBuilder._deRefFr(c, o.params[i]));
|
|
}
|
|
code.push(
|
|
c.call(
|
|
"Fr_" + o.fOp,
|
|
this.fnBuilder._getPtr(c, o.dLabel),
|
|
...params
|
|
)
|
|
);
|
|
} else if (o.op == "LOOP") {
|
|
code.push(
|
|
c.block(c.loop(
|
|
c.br_if(1, c.i32_eqz(c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])))),
|
|
o.body.build(c),
|
|
c.br(0)
|
|
))
|
|
);
|
|
} else if (o.op == "IF") {
|
|
code.push(
|
|
c.if(
|
|
c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])),
|
|
o.thenCode.build(c),
|
|
o.elseCode ? o.elseCode.build(c) : undefined
|
|
)
|
|
);
|
|
} else if (o.op == "FNCALL") {
|
|
const params = [];
|
|
for (let i=0; i<o.params.length; i++) {
|
|
params.push(this.fnBuilder._deRefFr(c, ["R", o.params[i]]));
|
|
}
|
|
code.push(
|
|
c.call(
|
|
o.fnName,
|
|
this.fnBuilder._getPtr(c, o.retLabel),
|
|
...params
|
|
)
|
|
);
|
|
} else if (o.op == "CHECKCONSTRAINT") {
|
|
code.push(
|
|
c.call(
|
|
"checkConstraint",
|
|
c.getLocal("cIdx"),
|
|
this.fnBuilder._deRefFr(c, o.a),
|
|
this.fnBuilder._deRefFr(c, o.b),
|
|
c.i32_const(this.fnBuilder.builder.module.allocString(o.strErr))
|
|
)
|
|
);
|
|
} else if (o.op == "CHECKASSERT") {
|
|
code.push(
|
|
c.call(
|
|
"checkAssert",
|
|
c.getLocal("cIdx"),
|
|
this.fnBuilder._deRefFr(c, o.a),
|
|
c.i32_const(this.fnBuilder.builder.module.allocString(o.strErr))
|
|
)
|
|
);
|
|
} else if (o.op == "LOG") {
|
|
code.push(
|
|
c.call(
|
|
"log",
|
|
this.fnBuilder._deRefFr(c, o.val)
|
|
)
|
|
);
|
|
}
|
|
});
|
|
return code;
|
|
}
|
|
}
|
|
|
|
class FunctionBuilderWasm {
|
|
|
|
constructor(builder, name, instanceDef, type, params) {
|
|
this.builder = builder;
|
|
this.name = name;
|
|
this.instanceDef = instanceDef;
|
|
this.type = type; // "COMPONENT" or "FUNCTION"
|
|
this.definedFrElements = [];
|
|
this.definedIntElements = [];
|
|
this.definedSizeElements = [];
|
|
this.definedPFrElements = [];
|
|
this.initializedElements = [];
|
|
this.initializedSignalOffset = [];
|
|
this.initializedSignalSizes = [];
|
|
|
|
this.refs = {};
|
|
|
|
this.nFr = 0;
|
|
this.nInt = 0;
|
|
this.nSizes = 0;
|
|
this.nPFr = 0;
|
|
|
|
}
|
|
|
|
defineFrElements(dLabel, size) {
|
|
this.refs[dLabel] = {
|
|
t: "Fr",
|
|
n: size,
|
|
idx: this.nFr
|
|
};
|
|
this.nFr += size;
|
|
}
|
|
|
|
defineIntElement(dLabel) {
|
|
this.refs[dLabel] = {
|
|
t: "I",
|
|
idx: this.nInt
|
|
};
|
|
this.nInt++;
|
|
}
|
|
|
|
defineSizesElement(dLabel) {
|
|
this.refs[dLabel] = {
|
|
t: "S",
|
|
idx: this.nSizes
|
|
};
|
|
this.nSizes++;
|
|
}
|
|
|
|
definePFrElement(dLabel) {
|
|
this.refs[dLabel] = {
|
|
t: "PFr",
|
|
idx: this.nPFr
|
|
};
|
|
this.nPFr++;
|
|
}
|
|
|
|
initializeFrElement(dLabel, offset, idConstant) {
|
|
this.initializedElements.push({dLabel, offset, idConstant});
|
|
}
|
|
|
|
initializeSignalOffset(dLabel, component, hash, hashLabel) {
|
|
this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
|
|
}
|
|
|
|
initializeSignalSizes(dLabel, component, hash, hashLabel) {
|
|
this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
|
|
}
|
|
|
|
setParams(params) {
|
|
assert(typeof this.params === "undefined");
|
|
assert(this.type === "FUNCTION");
|
|
this.params=params;
|
|
for (let i=0; i<this.params.length; i++) {
|
|
assert(typeof this.refs[this.params[i]] === "undefined");
|
|
this.refs[this.params[i]] = {
|
|
t: "P",
|
|
l: this.params[i]
|
|
};
|
|
}
|
|
}
|
|
|
|
_getPtr(c, label, offset) {
|
|
offset = offset || 0;
|
|
assert (typeof this.refs[label] !== "undefined");
|
|
if (this.refs[label].t == "Fr") {
|
|
return c.i32_add(
|
|
c.getLocal("sp"),
|
|
c.i32_const((this.refs[label].idx + offset) * this.builder.sizeFr)
|
|
);
|
|
} else if (this.refs[label].t == "I") {
|
|
assert(offset == 0);
|
|
return c.i32_add(
|
|
c.getLocal("sp"),
|
|
c.i32_const(
|
|
this.nFr * this.builder.sizeFr +
|
|
this.refs[label].idx * 4)
|
|
);
|
|
} else if (this.refs[label].t == "S") {
|
|
assert(offset == 0);
|
|
return c.i32_add(
|
|
c.getLocal("sp"),
|
|
c.i32_const(
|
|
this.nFr * this.builder.sizeFr +
|
|
this.nInt * 4 +
|
|
this.refs[label].idx * 4)
|
|
);
|
|
} else if (this.refs[label].t == "PFr") {
|
|
assert(offset == 0);
|
|
return c.i32_add(
|
|
c.getLocal("sp"),
|
|
c.i32_const(
|
|
this.nFr * this.builder.sizeFr +
|
|
this.nInt * 4 +
|
|
this.nSizes * 4 +
|
|
this.refs[label].idx * 4)
|
|
);
|
|
} else if (this.refs[label].t == "P") {
|
|
return c.i32_add(
|
|
c.getLocal(this.refs[label].l),
|
|
c.i32_const( offset * this.builder.sizeFr )
|
|
);
|
|
}
|
|
}
|
|
|
|
_getPtrConstant(c, idConstant) {
|
|
return c.i32_const( this.builder.pConstants + idConstant * this.builder.sizeFr );
|
|
}
|
|
|
|
_deRefInt(c, ref) {
|
|
if (ref[0] == "R") {
|
|
return c.call(
|
|
"Fr_toInt",
|
|
this._getPtr(c, ref[1])
|
|
);
|
|
} else if (ref[0] == "RI") {
|
|
return c.i32_load(this._getPtr(c, ref[1]));
|
|
} else if (ref[0] == "V") {
|
|
return c.i32_const(ref[1]);
|
|
} else if (ref[0] == "C") {
|
|
return c.call(
|
|
"Fr_toInt",
|
|
this._getPtrConstant(c, ref[1])
|
|
);
|
|
} else if (ref[0] == "CC") {
|
|
return c.getLocal("cIdx");
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
_deRefFr(c, ref) {
|
|
if (ref[0] == "R") {
|
|
if (this.refs[ref[1]].t == "Fr") {
|
|
return this._getPtr(c, ref[1]);
|
|
} else if (this.refs[ref[1]].t == "PFr") {
|
|
return c.i32_load(this._getPtr(c, ref[1]));
|
|
} else if (this.refs[ref[1]].t == "P") {
|
|
return c.getLocal(ref[1]);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
} else if (ref[0] == "C") {
|
|
return this._getPtrConstant(c, ref[1]);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
_reserveStack(c) {
|
|
const code = [];
|
|
code.push(
|
|
// Load SP
|
|
c.setLocal("sp", c.i32_load(c.i32_const(4))),
|
|
|
|
// Check we have enough memory
|
|
c.if(
|
|
c.i32_gt_u(
|
|
c.i32_const(this.nStackSize),
|
|
c.getLocal("sp")
|
|
),
|
|
c.call(
|
|
"error",
|
|
c.i32_const(errs.STACK_OUT_OF_MEM.code),
|
|
c.i32_const(errs.STACK_OUT_OF_MEM.pointer),
|
|
c.i32_const(0),
|
|
c.i32_const(0),
|
|
c.i32_const(0),
|
|
c.i32_const(0)
|
|
)
|
|
),
|
|
|
|
// Reserve space in sp
|
|
c.setLocal(
|
|
"sp",
|
|
c.i32_sub(
|
|
c.getLocal("sp"),
|
|
c.i32_const(this.nStackSize)
|
|
)
|
|
),
|
|
|
|
// Check if we have enought free memory
|
|
c.if(
|
|
c.i32_gt_u(
|
|
c.i32_load(c.i32_const(0)),
|
|
c.getLocal("sp")
|
|
),
|
|
c.call(
|
|
"error",
|
|
c.i32_const(errs.STACK_TOO_SMALL.code),
|
|
c.i32_const(errs.STACK_TOO_SMALL.pointer),
|
|
c.i32_const(0),
|
|
c.i32_const(0),
|
|
c.i32_const(0),
|
|
c.i32_const(0)
|
|
)
|
|
),
|
|
|
|
// Save sp
|
|
c.i32_store(c.i32_const(4), c.getLocal("sp"))
|
|
);
|
|
return code;
|
|
}
|
|
|
|
_freeStack(c) {
|
|
const code = [];
|
|
code.push(
|
|
c.i32_store(
|
|
c.i32_const(4),
|
|
c.i32_add(
|
|
c.getLocal("sp"),
|
|
c.i32_const(this.nStackSize)
|
|
)
|
|
)
|
|
);
|
|
return code;
|
|
}
|
|
|
|
_buildHeader(c) {
|
|
const code = [];
|
|
this.nStackSize =
|
|
this.nFr * this.builder.sizeFr +
|
|
this.nInt * 4 +
|
|
this.nSizes * 4 +
|
|
this.nPFr * 4;
|
|
|
|
|
|
code.push( this._reserveStack(c));
|
|
|
|
this.initializedElements.forEach( (o) => {
|
|
code.push(
|
|
c.call(
|
|
"Fr_copy",
|
|
this._getPtr(c, o.dLabel, o.offset),
|
|
this._getPtrConstant(c, o.idConstant)
|
|
)
|
|
);
|
|
});
|
|
this.initializedSignalOffset.forEach( (o) => {
|
|
code.push(
|
|
c.call(
|
|
"getSignalOffset",
|
|
this._getPtr(c, o.dLabel, o.offset),
|
|
this._deRefInt(c, o.component),
|
|
c.i64_const("0x" + o.hash)
|
|
)
|
|
);
|
|
});
|
|
this.initializedSignalSizes.forEach( (o) => {
|
|
code.push(
|
|
c.call(
|
|
"getSignalSizes",
|
|
this._getPtr(c, o.dLabel, o.offset),
|
|
this._deRefInt(c, o.component),
|
|
c.i64_const("0x" + o.hash)
|
|
)
|
|
);
|
|
});
|
|
return code;
|
|
}
|
|
|
|
_buildFooter(c) {
|
|
return this._freeStack(c);
|
|
}
|
|
|
|
newCodeBuilder() {
|
|
return new CodeBuilderWasm(this);
|
|
}
|
|
|
|
setBody(body) {
|
|
this.body = body;
|
|
}
|
|
|
|
build(module) {
|
|
|
|
|
|
const f = module.addFunction(this.name, this.instanceDef);
|
|
|
|
if (this.type=="COMPONENT") {
|
|
f.addParam("cIdx", "i32");
|
|
} else if (this.type=="FUNCTION") {
|
|
f.addParam("pRet", "i32");
|
|
for (let i=0;i<this.params.length;i++ ) {
|
|
f.addParam(this.params[i], "i32");
|
|
}
|
|
} else {
|
|
assert(false);
|
|
}
|
|
f.addLocal("sp", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
|
|
const code = [];
|
|
if (this.type=="COMPONENT") {
|
|
code.push(c.call("componentStarted", c.getLocal("cIdx")));
|
|
}
|
|
|
|
code.push(this._buildHeader(c));
|
|
code.push(this.body.build(c));
|
|
if (this.type=="COMPONENT") {
|
|
code.push(c.call("componentFinished", c.getLocal("cIdx")));
|
|
}
|
|
code.push(this._buildFooter(c));
|
|
|
|
f.addCode(flatArray(code));
|
|
|
|
function flatArray(c) {
|
|
const res=[];
|
|
for (let i=0; i<c.length; i++) {
|
|
if (Array.isArray(c[i])) {
|
|
res.push(...flatArray(c[i]));
|
|
} else {
|
|
res.push(c[i]);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class BuilderWasm {
|
|
constructor(p) {
|
|
this.F = new F1Field(p);
|
|
this.hashMaps={};
|
|
this.componentEntriesTables={};
|
|
this.sizes ={};
|
|
this.constants = [];
|
|
this.usedConstants = {};
|
|
this.functions = [];
|
|
this.components = [];
|
|
|
|
this.TYPE_SIGNAL = 1;
|
|
this.TYPE_COMPONENT = 2;
|
|
|
|
this.addConstant(Scalar.fromString("0")); // constants[0] = 0;
|
|
this.addConstant(Scalar.fromString("1")); // constants[1] = 1;
|
|
|
|
this.offsetComponentNInputSignals = 12;
|
|
this.sizeofComponent = 20;
|
|
}
|
|
|
|
setHeader(header) {
|
|
this.header=header;
|
|
|
|
this.n64 = Math.floor((Scalar.bitLength(this.header.P) - 1) / 64)+1;
|
|
this.sizeFr = this.n64*8 + 8;
|
|
}
|
|
|
|
// ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
|
|
addHashMap(name, table) {
|
|
this.hashMaps[name] = {table};
|
|
}
|
|
|
|
addComponentEntriesTable(name, cet) {
|
|
this.componentEntriesTables[name] = cet;
|
|
}
|
|
|
|
addSizes(name, table) {
|
|
this.sizes[name] = {table};
|
|
}
|
|
|
|
addConstant(c) {
|
|
c = this.F.e(c);
|
|
const cS = c.toString();
|
|
if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS];
|
|
this.constants.push(c);
|
|
this.usedConstants[cS] = this.constants.length - 1;
|
|
return this.constants.length - 1;
|
|
}
|
|
|
|
addFunction(fnBuilder) {
|
|
this.functions.push(fnBuilder);
|
|
}
|
|
|
|
addComponent(component) {
|
|
this.components.push(component);
|
|
}
|
|
|
|
setMapIsInput(map) {
|
|
this.mapIsInput = map;
|
|
}
|
|
|
|
setWit2Sig(wit2sig) {
|
|
this.wit2sig = wit2sig;
|
|
}
|
|
|
|
|
|
newComponentFunctionBuilder(name, instanceDef) {
|
|
return new FunctionBuilderWasm(this, name, instanceDef, "COMPONENT");
|
|
}
|
|
|
|
newFunctionBuilder(name, instanceDef, params) {
|
|
return new FunctionBuilderWasm(this, name, instanceDef, "FUNCTION", params);
|
|
}
|
|
|
|
|
|
// Body functions
|
|
|
|
_buildHeader(module) {
|
|
|
|
this.pCircuit = module.alloc(48);
|
|
|
|
this.pNSignals = this.pCircuit;
|
|
this.pNComponents = this.pCircuit + 4;
|
|
this.pNInputs = this.pCircuit + 8;
|
|
this.pNOutputs = this.pCircuit + 12;
|
|
this.pNVars = this.pCircuit + 16;
|
|
this.ppWit2sig = this.pCircuit + 20;
|
|
this.ppComponents = this.pCircuit + 24;
|
|
this.ppMapIsInput = this.pCircuit + 28;
|
|
this.ppConstants = this.pCircuit + 32;
|
|
this.ppSignals = this.pCircuit + 36;
|
|
this.ppInputSignalsToTrigger = this.pCircuit + 40;
|
|
this.ppSignalsAssigned = this.pCircuit + 44;
|
|
}
|
|
|
|
_buildSizes(module) {
|
|
for (let sName in this.sizes) {
|
|
const accSizes = this.sizes[sName];
|
|
|
|
const bytes = [];
|
|
|
|
for (let i=0; i<accSizes.table.length; i++) {
|
|
bytes.push(intToBytes32(accSizes.table[i]));
|
|
}
|
|
|
|
const fBytes = [].concat(...bytes);
|
|
accSizes.pointer = module.alloc(fBytes);
|
|
}
|
|
}
|
|
|
|
|
|
_buildHashMaps(module) {
|
|
|
|
for (let hmName in this.hashMaps ) {
|
|
const hm = this.hashMaps[hmName];
|
|
|
|
const bytes = [];
|
|
|
|
for (let i=0; i<256; i++) {
|
|
if (hm.table[i]) {
|
|
bytes.push(hexToBytesR(hm.table[i][0]));
|
|
bytes.push(intToBytes32(hm.table[i][1]));
|
|
} else {
|
|
bytes.push([0,0,0,0,0,0,0,0,0,0,0,0]);
|
|
}
|
|
}
|
|
|
|
const fBytes = [].concat(...bytes);
|
|
hm.pointer = module.alloc(fBytes);
|
|
}
|
|
|
|
}
|
|
|
|
_buildComponentEntriesTables(module) {
|
|
|
|
for (let cetName in this.componentEntriesTables) {
|
|
const cet = this.componentEntriesTables[cetName];
|
|
|
|
const bytes = [];
|
|
|
|
for (let j=0; j<cet.length; j++) {
|
|
const ty = cet[j].type == "S" ? this.TYPE_SIGNAL : this.TYPE_COMPONENT;
|
|
bytes.push(intToBytes32(cet[j].offset));
|
|
bytes.push(intToBytes32(this.sizes[cet[j].sizeName].pointer));
|
|
bytes.push(intToBytes32(ty));
|
|
}
|
|
|
|
const fBytes = [].concat(...bytes);
|
|
this.componentEntriesTables[cetName].pointer = module.alloc(fBytes);
|
|
}
|
|
}
|
|
|
|
|
|
_buildConstants(module) {
|
|
const self = this;
|
|
|
|
const bytes = [];
|
|
for (let i=0; i<self.constants.length; i++) {
|
|
bytes.push(Fr2Bytes(self.constants[i]));
|
|
}
|
|
|
|
const fBytes = [].concat(...bytes);
|
|
this.pConstants = module.alloc(fBytes);
|
|
|
|
|
|
function Fr2Bytes(n) {
|
|
const minShort = self.F.neg(self.F.e("80000000"));
|
|
const maxShort = self.F.e("7FFFFFFF", 16);
|
|
|
|
if ( (self.F.geq(n, minShort))
|
|
&&(self.F.leq(n, maxShort)))
|
|
{
|
|
if (self.F.geq(n, self.F.zero)) {
|
|
return shortMontgomeryPositive(n);
|
|
} else {
|
|
return shortMontgomeryNegative(n);
|
|
}
|
|
}
|
|
|
|
return longMontgomery(n);
|
|
|
|
|
|
function shortMontgomeryPositive(a) {
|
|
return [
|
|
...intToBytes32(Scalar.toNumber(a)),
|
|
...intToBytes32(0x40000000),
|
|
...long(toMontgomery(a))
|
|
];
|
|
}
|
|
|
|
|
|
function shortMontgomeryNegative(a) {
|
|
const b = -Scalar.toNumber(self.F.neg(a));
|
|
return [
|
|
...intToBytes32(b),
|
|
...intToBytes32(0x40000000),
|
|
...long(toMontgomery(a))
|
|
];
|
|
}
|
|
|
|
function longMontgomery(a) {
|
|
return [
|
|
...intToBytes32(0),
|
|
...intToBytes32(0xC0000000),
|
|
...long(toMontgomery(a))
|
|
];
|
|
}
|
|
|
|
function long(a) {
|
|
|
|
const bytes = [];
|
|
const arr = Scalar.toArray(a, 0x100000000);
|
|
for (let i=0; i<self.F.n64*2; i++) {
|
|
const idx = arr.length-1-i;
|
|
|
|
if ( idx >=0) {
|
|
bytes.push(...intToBytes32(arr[idx]));
|
|
} else {
|
|
bytes.push(...intToBytes32(0));
|
|
}
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
function toMontgomery(a) {
|
|
return self.F.mul(a, self.F.R);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
_buildFunctions(module) {
|
|
for (let i=0; i<this.functions.length; i++) {
|
|
const cfb = this.functions[i];
|
|
cfb.build(module);
|
|
}
|
|
}
|
|
|
|
_buildComponents(module) {
|
|
const bytes = new Array(this.components.length*5*4);
|
|
bytes.length=0;
|
|
for (let i=0; i<this.components.length; i++) {
|
|
const c = this.components[i];
|
|
|
|
bytes.push(...intToBytes32(this.hashMaps[c.hashMapName].pointer));
|
|
bytes.push(...intToBytes32(this.componentEntriesTables[c.entryTableName].pointer));
|
|
bytes.push(...intToBytes32(i));
|
|
bytes.push(...intToBytes32(c.nInSignals));
|
|
bytes.push(...intToBytes32(c.newThread ? 1 : 0));
|
|
|
|
module.addFunctionToTable(c.functionName);
|
|
}
|
|
|
|
this.pComponents = module.alloc(bytes);
|
|
}
|
|
|
|
_buildMapIsInput(module) {
|
|
const bytes = new Array(this.mapIsInput.length*4);
|
|
bytes.length=0;
|
|
for (let i=0; i<this.mapIsInput.length; i++) {
|
|
bytes.push(...intToBytes32(this.mapIsInput[i]));
|
|
}
|
|
|
|
this.pMapIsInput = module.alloc(bytes);
|
|
}
|
|
|
|
_buildWit2Sig(module) {
|
|
const bytes = new Array(this.wit2sig.length*4);
|
|
bytes.length =0;
|
|
for (let i=0; i<this.wit2sig.length; i++) {
|
|
bytes.push(...intToBytes32(this.wit2sig[i]));
|
|
}
|
|
|
|
this.pWit2sig = module.alloc(bytes);
|
|
}
|
|
|
|
_buildCircuitVar(module) {
|
|
module.addData(this.pNSignals, intToBytes32(this.header.NSignals));
|
|
module.addData(this.pNComponents, intToBytes32(this.header.NComponents));
|
|
module.addData(this.pNInputs, intToBytes32(this.header.NInputs));
|
|
module.addData(this.pNOutputs, intToBytes32(this.header.NOutputs));
|
|
module.addData(this.pNVars, intToBytes32(this.header.NVars));
|
|
module.addData(this.ppWit2sig, intToBytes32(this.pWit2sig));
|
|
module.addData(this.ppComponents, intToBytes32(this.pComponents));
|
|
module.addData(this.ppMapIsInput, intToBytes32(this.pMapIsInput));
|
|
module.addData(this.ppConstants, intToBytes32(this.pConstants));
|
|
module.addData(this.ppSignals, intToBytes32(this.pSignals));
|
|
module.addData(this.ppInputSignalsToTrigger, intToBytes32(this.pInputSignalsToTrigger));
|
|
module.addData(this.ppSignalsAssigned, intToBytes32(this.pSignalsAssigned));
|
|
|
|
}
|
|
|
|
_buildErrors(module) {
|
|
for (let e in errs) {
|
|
errs[e].pointer = module.allocString(errs[e].str);
|
|
}
|
|
}
|
|
|
|
|
|
async build(fd, outType) {
|
|
const encoder = new TextEncoder("utf-8");
|
|
let module;
|
|
if (outType == "wasm") {
|
|
module=new ModuleBuilder();
|
|
} else if (outType == "wat") {
|
|
module=new ModuleBuilderWat();
|
|
} else {
|
|
assert(false);
|
|
}
|
|
this.module = module;
|
|
|
|
// First of all reseve the space for the header so this has a fixed position starting at 8.
|
|
this._buildHeader(module);
|
|
this._buildErrors(module);
|
|
buildRuntime(module, this);
|
|
this._buildSizes(module);
|
|
this._buildConstants(module);
|
|
this._buildHashMaps(module);
|
|
this._buildComponentEntriesTables(module);
|
|
this._buildFunctions(module);
|
|
this._buildComponents(module);
|
|
this._buildMapIsInput(module);
|
|
this._buildWit2Sig(module);
|
|
this._buildCircuitVar(module);
|
|
|
|
module.setMemory(2000);
|
|
if (outType == "wasm") {
|
|
const bytes = module.build();
|
|
const bytesArr = new Uint8Array(bytes);
|
|
await fd.write(bytesArr);
|
|
} else if (outType == "wat") {
|
|
const code = module.build();
|
|
await writeCode(code);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
|
|
async function writeCode(c) {
|
|
if (c.push) {
|
|
for (let i=0; i<c.length; i++) {
|
|
await writeCode(c[i]);
|
|
}
|
|
} else if (typeof c === "string") {
|
|
await fd.write(encoder.encode(c + "\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
module.exports = BuilderWasm;
|