From d97bc9b61cefb60eaa28c360d07acf1fc04fa084 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Fri, 24 Jul 2020 22:02:30 +0200 Subject: [PATCH] Assembly backend (#69) * Proof-of-Concept Assembly code generator * Tag inline per procedure so we can easily track the tradeoff on tower fields * Implement Assembly for modular addition (but very curious off-by-one) * Fix off-by one for moduli with non msb set * Stash (super fast) alternative but still off by carry * Fix GCC optimizing ASM away * Save 1 register to allow compiling for BLS12-381 (in the GMP test) * The compiler cannot find enough registers if the ASM file is not compiled with -O3 * Add modsub * Add field negation * Implement no-carry Assembly optimized field multiplication * Expose UseX86ASM to the EC benchmark * omit frame pointer to save registers instead of hardcoding -O3. Also ensure early clobber constraints for Clang * Prepare for assembly fallback * Implement fallback for CPU that don't support ADX and BMI2 * Add CPU runtime detection * Update README closes #66 * Remove commented out code --- .travis.yml | 3 + README.md | 117 ++- azure-pipelines.yml | 11 +- benchmarks/bench_ec_g1.nim | 6 +- benchmarks/bench_ec_g2.nim | 6 +- benchmarks/bench_elliptic_template.nim | 18 +- benchmarks/bench_fields_template.nim | 18 +- benchmarks/bench_fp.nim | 6 +- benchmarks/bench_fp12.nim | 6 +- benchmarks/bench_fp2.nim | 6 +- benchmarks/bench_fp6.nim | 6 +- constantine.nimble | 41 +- constantine/arithmetic/finite_fields.nim | 147 ++-- .../arithmetic/finite_fields_asm_mul_x86.nim | 223 +++++ .../finite_fields_asm_mul_x86_adx_bmi2.nim | 282 +++++++ .../arithmetic/finite_fields_asm_x86.nim | 340 ++++++++ constantine/arithmetic/limbs.nim | 3 - constantine/arithmetic/limbs_montgomery.nim | 56 +- constantine/config/common.nim | 5 + constantine/primitives.nim | 4 + constantine/primitives/README.md | 58 +- constantine/primitives/cpuinfo_x86.nim | 779 ++++++++++++++++++ .../primitives/macro_assembler_x86.nim | 620 ++++++++++++++ constantine/primitives/research/README.md | 45 - .../research/addcarry_subborrow_compiler.nim | 133 --- tests/t_ec_sage_bls12_381 | Bin 491008 -> 0 bytes tests/t_finite_fields_vs_gmp.nim | 21 +- tests/t_io_fields.nim | 8 + 28 files changed, 2601 insertions(+), 367 deletions(-) create mode 100644 constantine/arithmetic/finite_fields_asm_mul_x86.nim create mode 100644 constantine/arithmetic/finite_fields_asm_mul_x86_adx_bmi2.nim create mode 100644 constantine/arithmetic/finite_fields_asm_x86.nim create mode 100644 constantine/primitives/cpuinfo_x86.nim create mode 100644 constantine/primitives/macro_assembler_x86.nim delete mode 100644 constantine/primitives/research/README.md delete mode 100644 constantine/primitives/research/addcarry_subborrow_compiler.nim delete mode 100755 tests/t_ec_sage_bls12_381 diff --git a/.travis.yml b/.travis.yml index aa55f63..5612fe8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -99,6 +99,9 @@ script: - nimble refresh - nimble install gmp stew - nimble test_parallel + - if [[ "$ARCH" != "arm64" ]]; then + nimble test_parallel_no_assembler; + fi branches: except: - gh-pages diff --git a/README.md b/README.md index a6de283..cf5909a 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,18 @@ You can install the developement version of the library through nimble with the nimble install https://github.com/mratsim/constantine@#master ``` -For speed it is recommended to prefer Clang, MSVC or ICC over GCC. -GCC does not properly optimize add-with-carry and sub-with-borrow loops (see [Compiler-caveats](#Compiler-caveats)). +For speed it is recommended to prefer Clang, MSVC or ICC over GCC (see [Compiler-caveats](#Compiler-caveats)). Further if using GCC, GCC 7 at minimum is required, previous versions generated incorrect add-with-carry code. +On x86-64, inline assembly is used to workaround compilers having issues optimizing large integer arithmetic, +and also ensure constant-time code. +This can be deactivated with `"-d:ConstantineASM=false"`: +- at a significant performance cost with GCC (~50% slower than Clang). +- at misssed opportunity on recent CPUs that support MULX/ADCX/ADOX instructions (~60% faster than Clang). +- There is a 2.4x perf ratio between using plain GCC vs GCC with inline assembly. + ## Target audience The library aims to be a portable, compact and hardened library for elliptic curve cryptography needs, in particular for blockchain protocols and zero-knowledge proofs system. @@ -39,10 +45,13 @@ in this order ## Curves supported At the moment the following curves are supported, adding a new curve only requires adding the prime modulus -and its bitsize in [constantine/config/curves.nim](constantine/config/curves.nim). +and its bitsize in [constantine/config/curves.nim](constantine/config/curves_declaration.nim). The following curves are configured: +> Note: At the moment, finite field arithmetic is fully supported +> but elliptic curve arithmetic is work-in-progress. + ### ECDH / ECDSA curves - NIST P-224 @@ -58,7 +67,8 @@ Families: - FKM: Fotiadis-Konstantinou-Martindale Curves: -- BN254 (Zero-Knowledge Proofs, Snarks, Starks, Zcash, Ethereum 1) +- BN254_Nogami +- BN254_Snarks (Zero-Knowledge Proofs, Snarks, Starks, Zcash, Ethereum 1) - BLS12-377 (Zexe) - BLS12-381 (Algorand, Chia Networks, Dfinity, Ethereum 2, Filecoin, Zcash Sapling) - BN446 @@ -137,8 +147,13 @@ To measure the performance of Constantine ```bash git clone https://github.com/mratsim/constantine -nimble bench_fp_clang -nimble bench_fp2_clang +nimble bench_fp # Using Assembly (+ GCC) +nimble bench_fp_clang # Using Clang only +nimble bench_fp_gcc # Using Clang only (very slow) +nimble bench_fp2 +# ... +nimble bench_ec_g1 +nimble bench_ec_g2 ``` As mentioned in the [Compiler caveats](#compiler-caveats) section, GCC is up to 2x slower than Clang due to mishandling of carries and register usage. @@ -146,33 +161,51 @@ As mentioned in the [Compiler caveats](#compiler-caveats) section, GCC is up to On my machine, for selected benchmarks on the prime field for popular pairing-friendly curves. ``` -⚠️ Measurements are approximate and use the CPU nominal clock: Turbo-Boost and overclocking will skew them. -========================================================================================================== +Compiled with GCC +Optimization level => + no optimization: false + release: true + danger: true + inline assembly: true +Using Constantine with 64-bit limbs +Running on Intel(R) Core(TM) i9-9980XE CPU @ 3.00GHz -All benchmarks are using constant-time implementations to protect against side-channel attacks. +⚠️ Cycles measurements are approximate and use the CPU nominal clock: Turbo-Boost and overclocking will skew them. +i.e. a 20% overclock will be about 20% off (assuming no dynamic frequency scaling) -Compiled with Clang -Running on Intel(R) Core(TM) i9-9980XE CPU @ 3.00GHz (overclocked all-core Turbo @4.1GHz) - --------------------------------------------------------------------------------- -Addition Fp[BN254] 0 ns 0 cycles -Substraction Fp[BN254] 0 ns 0 cycles -Negation Fp[BN254] 0 ns 0 cycles -Multiplication Fp[BN254] 21 ns 65 cycles -Squaring Fp[BN254] 18 ns 55 cycles -Inversion Fp[BN254] 6266 ns 18799 cycles --------------------------------------------------------------------------------- -Addition Fp[BLS12_381] 0 ns 0 cycles -Substraction Fp[BLS12_381] 0 ns 0 cycles -Negation Fp[BLS12_381] 0 ns 0 cycles -Multiplication Fp[BLS12_381] 45 ns 136 cycles -Squaring Fp[BLS12_381] 39 ns 118 cycles -Inversion Fp[BLS12_381] 15683 ns 47050 cycles --------------------------------------------------------------------------------- +================================================================================================================= +------------------------------------------------------------------------------------------------------------------------------------------------- +Addition Fp[BN254_Snarks] 333333333.333 ops/s 3 ns/op 9 CPU cycles (approx) +Substraction Fp[BN254_Snarks] 500000000.000 ops/s 2 ns/op 8 CPU cycles (approx) +Negation Fp[BN254_Snarks] 1000000000.000 ops/s 1 ns/op 3 CPU cycles (approx) +Multiplication Fp[BN254_Snarks] 71428571.429 ops/s 14 ns/op 44 CPU cycles (approx) +Squaring Fp[BN254_Snarks] 71428571.429 ops/s 14 ns/op 44 CPU cycles (approx) +Inversion (constant-time Euclid) Fp[BN254_Snarks] 122579.063 ops/s 8158 ns/op 24474 CPU cycles (approx) +Inversion via exponentiation p-2 (Little Fermat) Fp[BN254_Snarks] 153822.489 ops/s 6501 ns/op 19504 CPU cycles (approx) +Square Root + square check (constant-time) Fp[BN254_Snarks] 153491.942 ops/s 6515 ns/op 19545 CPU cycles (approx) +Exp curve order (constant-time) - 254-bit Fp[BN254_Snarks] 104580.632 ops/s 9562 ns/op 28687 CPU cycles (approx) +Exp curve order (Leak exponent bits) - 254-bit Fp[BN254_Snarks] 153798.831 ops/s 6502 ns/op 19506 CPU cycles (approx) +------------------------------------------------------------------------------------------------------------------------------------------------- +Addition Fp[BLS12_381] 250000000.000 ops/s 4 ns/op 14 CPU cycles (approx) +Substraction Fp[BLS12_381] 250000000.000 ops/s 4 ns/op 13 CPU cycles (approx) +Negation Fp[BLS12_381] 1000000000.000 ops/s 1 ns/op 4 CPU cycles (approx) +Multiplication Fp[BLS12_381] 35714285.714 ops/s 28 ns/op 84 CPU cycles (approx) +Squaring Fp[BLS12_381] 35714285.714 ops/s 28 ns/op 85 CPU cycles (approx) +Inversion (constant-time Euclid) Fp[BLS12_381] 43763.676 ops/s 22850 ns/op 68552 CPU cycles (approx) +Inversion via exponentiation p-2 (Little Fermat) Fp[BLS12_381] 63983.620 ops/s 15629 ns/op 46889 CPU cycles (approx) +Square Root + square check (constant-time) Fp[BLS12_381] 63856.960 ops/s 15660 ns/op 46982 CPU cycles (approx) +Exp curve order (constant-time) - 255-bit Fp[BLS12_381] 68535.399 ops/s 14591 ns/op 43775 CPU cycles (approx) +Exp curve order (Leak exponent bits) - 255-bit Fp[BLS12_381] 93222.709 ops/s 10727 ns/op 32181 CPU cycles (approx) +------------------------------------------------------------------------------------------------------------------------------------------------- Notes: - GCC is significantly slower than Clang on multiprecision arithmetic. - The simplest operations might be optimized away by the compiler. + - Compilers: + Compilers are severely limited on multiprecision arithmetic. + Inline Assembly is used by default (nimble bench_fp). + Bench without assembly can use "nimble bench_fp_gcc" or "nimble bench_fp_clang". + GCC is significantly slower than Clang on multiprecision arithmetic due to catastrophic handling of carries. + - The simplest operations might be optimized away by the compiler. + - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits) ``` ### Compiler caveats @@ -234,25 +267,15 @@ add256: retq ``` +As a workaround key procedures use inline assembly. + ### Inline assembly -Constantine uses inline assembly for a very restricted use-case: "conditional mov", -and a temporary use-case "hardware 128-bit division" that will be replaced ASAP (as hardware division is not constant-time). +While using intrinsics significantly improve code readability, portability, auditability and maintainability, +Constantine use inline assembly on x86-64 to ensure performance portability despite poor optimization (for GCC) +and also to use dedicated large integer instructions MULX, ADCX, ADOX that compilers cannot generate. -Using intrinsics otherwise significantly improve code readability, portability, auditability and maintainability. - -#### Future optimizations - -In the future more inline assembly primitives might be added provided the performance benefit outvalues the significant complexity. -In particular, multiprecision multiplication and squaring on x86 can use the instructions MULX, ADCX and ADOX -to multiply-accumulate on 2 carry chains in parallel (with instruction-level parallelism) -and improve performance by 15~20% over an uint128-based implementation. -As no compiler is able to generate such code even when using the `_mulx_u64` and `_addcarryx_u64` intrinsics, -either the assembly for each supported bigint size must be hardcoded -or a "compiler" must be implemented in macros that will generate the required inline assembly at compile-time. - -Such a compiler can also be used to overcome GCC codegen deficiencies, here is an example for add-with-carry: -https://github.com/mratsim/finite-fields/blob/d7f6d8bb/macro_add_carry.nim +The speed improvement on finite field arithmetic is up 60% with MULX, ADCX, ADOX on BLS12-381 (6 limbs). ## Sizes: code size, stack usage @@ -286,3 +309,7 @@ or * Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0) at your option. This file may not be copied, modified, or distributed except according to those terms. + +This library has **no external dependencies**. +In particular GMP is used only for testing and differential fuzzing +and is not linked in the library. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 38ed9d4..8817707 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -186,12 +186,19 @@ steps: echo "PATH=${PATH}" export ucpu=${UCPU} nimble test_parallel - displayName: 'Testing the package (including GMP)' + displayName: 'Testing Constantine with Assembler and with GMP' + condition: ne(variables['Agent.OS'], 'Windows_NT') + + - bash: | + echo "PATH=${PATH}" + export ucpu=${UCPU} + nimble test_parallel_no_assembler + displayName: 'Testing Constantine without Assembler and with GMP' condition: ne(variables['Agent.OS'], 'Windows_NT') - bash: | echo "PATH=${PATH}" export ucpu=${UCPU} nimble test_no_gmp - displayName: 'Testing the package (without GMP)' + displayName: 'Testing the package (without Assembler or GMP)' condition: eq(variables['Agent.OS'], 'Windows_NT') diff --git a/benchmarks/bench_ec_g1.nim b/benchmarks/bench_ec_g1.nim index 6389353..1bf2ddf 100644 --- a/benchmarks/bench_ec_g1.nim +++ b/benchmarks/bench_ec_g1.nim @@ -64,8 +64,4 @@ proc main() = separator() main() - -echo "\nNotes:" -echo " - GCC is significantly slower than Clang on multiprecision arithmetic." -echo " - The simplest operations might be optimized away by the compiler." -echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" +notes() diff --git a/benchmarks/bench_ec_g2.nim b/benchmarks/bench_ec_g2.nim index 6fb16f2..7207fa9 100644 --- a/benchmarks/bench_ec_g2.nim +++ b/benchmarks/bench_ec_g2.nim @@ -65,8 +65,4 @@ proc main() = separator() main() - -echo "\nNotes:" -echo " - GCC is significantly slower than Clang on multiprecision arithmetic." -echo " - The simplest operations might be optimized away by the compiler." -echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" +notes() diff --git a/benchmarks/bench_elliptic_template.nim b/benchmarks/bench_elliptic_template.nim index 3728acf..fb746ef 100644 --- a/benchmarks/bench_elliptic_template.nim +++ b/benchmarks/bench_elliptic_template.nim @@ -14,7 +14,7 @@ import # Internals - ../constantine/config/curves, + ../constantine/config/[curves, common], ../constantine/arithmetic, ../constantine/io/io_bigints, ../constantine/elliptic/[ec_weierstrass_projective, ec_scalar_mul, ec_endomorphism_accel], @@ -57,7 +57,11 @@ elif defined(icc): else: echo "\nCompiled with an unknown compiler" -echo "Optimization level => no optimization: ", not defined(release), " | release: ", defined(release), " | danger: ", defined(danger) +echo "Optimization level => " +echo " no optimization: ", not defined(release) +echo " release: ", defined(release) +echo " danger: ", defined(danger) +echo " inline assembly: ", UseX86ASM when (sizeof(int) == 4) or defined(Constantine32): echo "⚠️ Warning: using Constantine with 32-bit limbs" @@ -84,6 +88,16 @@ proc report(op, elliptic: string, start, stop: MonoTime, startClk, stopClk: int6 else: echo &"{op:<60} {elliptic:<40} {throughput:>15.3f} ops/s {ns:>9} ns/op" +proc notes*() = + echo "Notes:" + echo " - Compilers:" + echo " Compilers are severely limited on multiprecision arithmetic." + echo " Inline Assembly is used by default (nimble bench_fp)." + echo " Bench without assembly can use \"nimble bench_fp_gcc\" or \"nimble bench_fp_clang\"." + echo " GCC is significantly slower than Clang on multiprecision arithmetic due to catastrophic handling of carries." + echo " - The simplest operations might be optimized away by the compiler." + echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" + macro fixEllipticDisplay(T: typedesc): untyped = # At compile-time, enums are integers and their display is buggy # we get the Curve ID instead of the curve name. diff --git a/benchmarks/bench_fields_template.nim b/benchmarks/bench_fields_template.nim index 9135c23..e17bc4a 100644 --- a/benchmarks/bench_fields_template.nim +++ b/benchmarks/bench_fields_template.nim @@ -14,7 +14,7 @@ import # Internals - ../constantine/config/curves, + ../constantine/config/[curves, common], ../constantine/arithmetic, ../constantine/towers, # Helpers @@ -54,7 +54,11 @@ elif defined(icc): else: echo "\nCompiled with an unknown compiler" -echo "Optimization level => no optimization: ", not defined(release), " | release: ", defined(release), " | danger: ", defined(danger) +echo "Optimization level => " +echo " no optimization: ", not defined(release) +echo " release: ", defined(release) +echo " danger: ", defined(danger) +echo " inline assembly: ", UseX86ASM when (sizeof(int) == 4) or defined(Constantine32): echo "⚠️ Warning: using Constantine with 32-bit limbs" @@ -81,6 +85,16 @@ proc report(op, field: string, start, stop: MonoTime, startClk, stopClk: int64, else: echo &"{op:<50} {field:<18} {throughput:>15.3f} ops/s {ns:>9} ns/op" +proc notes*() = + echo "Notes:" + echo " - Compilers:" + echo " Compilers are severely limited on multiprecision arithmetic." + echo " Inline Assembly is used by default (nimble bench_fp)." + echo " Bench without assembly can use \"nimble bench_fp_gcc\" or \"nimble bench_fp_clang\"." + echo " GCC is significantly slower than Clang on multiprecision arithmetic due to catastrophic handling of carries." + echo " - The simplest operations might be optimized away by the compiler." + echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" + macro fixFieldDisplay(T: typedesc): untyped = # At compile-time, enums are integers and their display is buggy # we get the Curve ID instead of the curve name. diff --git a/benchmarks/bench_fp.nim b/benchmarks/bench_fp.nim index 5ec224d..ea510ee 100644 --- a/benchmarks/bench_fp.nim +++ b/benchmarks/bench_fp.nim @@ -59,8 +59,4 @@ proc main() = separator() main() - -echo "Notes:" -echo " - GCC is significantly slower than Clang on multiprecision arithmetic." -echo " - The simplest operations might be optimized away by the compiler." -echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" +notes() diff --git a/benchmarks/bench_fp12.nim b/benchmarks/bench_fp12.nim index f97bb56..e09efe3 100644 --- a/benchmarks/bench_fp12.nim +++ b/benchmarks/bench_fp12.nim @@ -50,8 +50,4 @@ proc main() = separator() main() - -echo "Notes:" -echo " - GCC is significantly slower than Clang on multiprecision arithmetic." -echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" -echo " - The tower of extension fields chosen can lead to a large difference of performance between primes of similar bitwidth." +notes() diff --git a/benchmarks/bench_fp2.nim b/benchmarks/bench_fp2.nim index db5579e..52d4b7c 100644 --- a/benchmarks/bench_fp2.nim +++ b/benchmarks/bench_fp2.nim @@ -51,8 +51,4 @@ proc main() = separator() main() - -echo "Notes:" -echo " - GCC is significantly slower than Clang on multiprecision arithmetic." -echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" -echo " - The tower of extension fields chosen can lead to a large difference of performance between primes of similar bitwidth." +notes() diff --git a/benchmarks/bench_fp6.nim b/benchmarks/bench_fp6.nim index 06577de..717e1f6 100644 --- a/benchmarks/bench_fp6.nim +++ b/benchmarks/bench_fp6.nim @@ -50,8 +50,4 @@ proc main() = separator() main() - -echo "Notes:" -echo " - GCC is significantly slower than Clang on multiprecision arithmetic." -echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" -echo " - The tower of extension fields chosen can lead to a large difference of performance between primes of similar bitwidth." +notes() diff --git a/constantine.nimble b/constantine.nimble index 5579c87..ae409e9 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -106,7 +106,7 @@ proc runBench(benchName: string, compiler = "") = var cc = "" if compiler != "": - cc = "--cc:" & compiler + cc = "--cc:" & compiler & " -d:ConstantineASM=false" exec "nim c " & cc & " -d:danger --verbosity:0 -o:build/" & benchName & "_" & compiler & " -r --hints:off --warnings:off benchmarks/" & benchName & ".nim" @@ -209,6 +209,45 @@ task test_parallel, "Run all tests in parallel (via GNU parallel)": runBench("bench_ec_g1") runBench("bench_ec_g2") +task test_parallel_no_assembler, "Run all tests (without macro assembler) in parallel (via GNU parallel)": + # -d:testingCurves is configured in a *.nim.cfg for convenience + let cmdFile = true # open(buildParallel, mode = fmWrite) # Nimscript doesn't support IO :/ + exec "> " & buildParallel + + for td in testDesc: + if td.path in useDebug: + test "-d:debugConstantine -d:ConstantineASM=false", td.path, cmdFile + else: + test " -d:ConstantineASM=false", td.path, cmdFile + + # cmdFile.close() + # Execute everything in parallel with GNU parallel + exec "parallel --keep-order --group < " & buildParallel + + exec "> " & buildParallel + if sizeof(int) == 8: # 32-bit tests on 64-bit arch + for td in testDesc: + if td.path in useDebug: + test "-d:Constantine32 -d:debugConstantine -d:ConstantineASM=false", td.path, cmdFile + else: + test "-d:Constantine32 -d:ConstantineASM=false", td.path, cmdFile + # cmdFile.close() + # Execute everything in parallel with GNU parallel + exec "parallel --keep-order --group < " & buildParallel + + # Now run the benchmarks + # + # Benchmarks compile and run + # ignore Windows 32-bit for the moment + # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment + if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): + runBench("bench_fp") + runBench("bench_fp2") + runBench("bench_fp6") + runBench("bench_fp12") + runBench("bench_ec_g1") + runBench("bench_ec_g2") + task test_parallel_no_gmp, "Run all tests in parallel (via GNU parallel)": # -d:testingCurves is configured in a *.nim.cfg for convenience let cmdFile = true # open(buildParallel, mode = fmWrite) # Nimscript doesn't support IO :/ diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index 00190d5..d85d822 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -29,11 +29,13 @@ import ../config/[common, type_fp, curves], ./bigints, ./limbs_montgomery +when UseX86ASM: + import ./finite_fields_asm_x86 + export Fp # No exceptions allowed {.push raises: [].} -{.push inline.} # ############################################################ # @@ -41,15 +43,15 @@ export Fp # # ############################################################ -func fromBig*[C: static Curve](T: type Fp[C], src: BigInt): Fp[C] {.noInit.} = +func fromBig*[C: static Curve](T: type Fp[C], src: BigInt): Fp[C] {.noInit, inline.} = ## Convert a BigInt to its Montgomery form result.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) -func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) = +func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) {.inline.}= ## Convert a BigInt to its Montgomery form dst.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) -func toBig*(src: Fp): auto {.noInit.} = +func toBig*(src: Fp): auto {.noInit, inline.} = ## Convert a finite-field element to a BigInt in natural representation var r {.noInit.}: typeof(src.mres) r.redc(src.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) @@ -58,14 +60,17 @@ func toBig*(src: Fp): auto {.noInit.} = # Copy # ------------------------------------------------------------ -func ccopy*(a: var Fp, b: Fp, ctl: SecretBool) = +func ccopy*(a: var Fp, b: Fp, ctl: SecretBool) {.inline.} = ## Constant-time conditional copy ## If ctl is true: b is copied into a ## if ctl is false: b is not copied and a is unmodified ## Time and memory accesses are the same whether a copy occurs or not - ccopy(a.mres, b.mres, ctl) + when UseX86ASM: + ccopy_asm(a.mres.limbs, b.mres.limbs, ctl) + else: + ccopy(a.mres, b.mres, ctl) -func cswap*(a, b: var Fp, ctl: CTBool) = +func cswap*(a, b: var Fp, ctl: CTBool) {.inline.} = ## Swap ``a`` and ``b`` if ``ctl`` is true ## ## Constant-time: @@ -93,80 +98,108 @@ func cswap*(a, b: var Fp, ctl: CTBool) = # In practice I'm not aware of such prime being using in elliptic curves. # 2^127 - 1 and 2^521 - 1 are used but 127 and 521 are not multiple of 32/64 -func `==`*(a, b: Fp): SecretBool = +func `==`*(a, b: Fp): SecretBool {.inline.} = ## Constant-time equality check a.mres == b.mres -func isZero*(a: Fp): SecretBool = +func isZero*(a: Fp): SecretBool {.inline.} = ## Constant-time check if zero a.mres.isZero() -func isOne*(a: Fp): SecretBool = +func isOne*(a: Fp): SecretBool {.inline.} = ## Constant-time check if one a.mres == Fp.C.getMontyOne() -func setZero*(a: var Fp) = +func setZero*(a: var Fp) {.inline.} = ## Set ``a`` to zero a.mres.setZero() -func setOne*(a: var Fp) = +func setOne*(a: var Fp) {.inline.} = ## Set ``a`` to one # Note: we need 1 in Montgomery residue form # TODO: Nim codegen is not optimal it uses a temporary # Check if the compiler optimizes it away a.mres = Fp.C.getMontyOne() -func `+=`*(a: var Fp, b: Fp) = +func `+=`*(a: var Fp, b: Fp) {.inline.} = ## In-place addition modulo p - var overflowed = add(a.mres, b.mres) - overflowed = overflowed or not(a.mres < Fp.C.Mod) - discard csub(a.mres, Fp.C.Mod, overflowed) + when UseX86ASM and a.mres.limbs.len <= 6: # TODO: handle spilling + addmod_asm(a.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + else: + var overflowed = add(a.mres, b.mres) + overflowed = overflowed or not(a.mres < Fp.C.Mod) + discard csub(a.mres, Fp.C.Mod, overflowed) -func `-=`*(a: var Fp, b: Fp) = +func `-=`*(a: var Fp, b: Fp) {.inline.} = ## In-place substraction modulo p - let underflowed = sub(a.mres, b.mres) - discard cadd(a.mres, Fp.C.Mod, underflowed) + when UseX86ASM and a.mres.limbs.len <= 6: # TODO: handle spilling + submod_asm(a.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + else: + let underflowed = sub(a.mres, b.mres) + discard cadd(a.mres, Fp.C.Mod, underflowed) -func double*(a: var Fp) = +func double*(a: var Fp) {.inline.} = ## Double ``a`` modulo p - var overflowed = double(a.mres) - overflowed = overflowed or not(a.mres < Fp.C.Mod) - discard csub(a.mres, Fp.C.Mod, overflowed) + when UseX86ASM and a.mres.limbs.len <= 6: # TODO: handle spilling + addmod_asm(a.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs) + else: + var overflowed = double(a.mres) + overflowed = overflowed or not(a.mres < Fp.C.Mod) + discard csub(a.mres, Fp.C.Mod, overflowed) -func sum*(r: var Fp, a, b: Fp) = +func sum*(r: var Fp, a, b: Fp) {.inline.} = ## Sum ``a`` and ``b`` into ``r`` module p ## r is initialized/overwritten - var overflowed = r.mres.sum(a.mres, b.mres) - overflowed = overflowed or not(r.mres < Fp.C.Mod) - discard csub(r.mres, Fp.C.Mod, overflowed) + when UseX86ASM and a.mres.limbs.len <= 6: # TODO: handle spilling + r = a + addmod_asm(r.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + else: + var overflowed = r.mres.sum(a.mres, b.mres) + overflowed = overflowed or not(r.mres < Fp.C.Mod) + discard csub(r.mres, Fp.C.Mod, overflowed) -func diff*(r: var Fp, a, b: Fp) = +func diff*(r: var Fp, a, b: Fp) {.inline.} = ## Substract `b` from `a` and store the result into `r`. ## `r` is initialized/overwritten - var underflowed = r.mres.diff(a.mres, b.mres) - discard cadd(r.mres, Fp.C.Mod, underflowed) + when UseX86ASM and a.mres.limbs.len <= 6: # TODO: handle spilling + var t = a # Handle aliasing r == b + submod_asm(t.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + r = t + else: + var underflowed = r.mres.diff(a.mres, b.mres) + discard cadd(r.mres, Fp.C.Mod, underflowed) -func double*(r: var Fp, a: Fp) = +func double*(r: var Fp, a: Fp) {.inline.} = ## Double ``a`` into ``r`` ## `r` is initialized/overwritten - var overflowed = r.mres.double(a.mres) - overflowed = overflowed or not(r.mres < Fp.C.Mod) - discard csub(r.mres, Fp.C.Mod, overflowed) + when UseX86ASM and a.mres.limbs.len <= 6: # TODO: handle spilling + r = a + addmod_asm(r.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs) + else: + var overflowed = r.mres.double(a.mres) + overflowed = overflowed or not(r.mres < Fp.C.Mod) + discard csub(r.mres, Fp.C.Mod, overflowed) -func prod*(r: var Fp, a, b: Fp) = +func prod*(r: var Fp, a, b: Fp) {.inline.} = ## Store the product of ``a`` by ``b`` modulo p into ``r`` ## ``r`` is initialized / overwritten r.mres.montyMul(a.mres, b.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) -func square*(r: var Fp, a: Fp) = +func square*(r: var Fp, a: Fp) {.inline.} = ## Squaring modulo p r.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) -func neg*(r: var Fp, a: Fp) = +func neg*(r: var Fp, a: Fp) {.inline.} = ## Negate modulo p - discard r.mres.diff(Fp.C.Mod, a.mres) + when UseX86ASM and defined(gcc): + # Clang and every compiler besides GCC + # can cleanly optimized this + # especially on Fp2 + negmod_asm(r.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs) + else: + discard r.mres.diff(Fp.C.Mod, a.mres) -func div2*(a: var Fp) = +func div2*(a: var Fp) {.inline.} = ## Modular division by 2 a.mres.div2_modular(Fp.C.getPrimePlus1div2()) @@ -178,7 +211,7 @@ func div2*(a: var Fp) = # # Internally those procedures will allocate extra scratchspace on the stack -func pow*(a: var Fp, exponent: BigInt) = +func pow*(a: var Fp, exponent: BigInt) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer @@ -191,7 +224,7 @@ func pow*(a: var Fp, exponent: BigInt) = Fp.C.canUseNoCarryMontySquare() ) -func pow*(a: var Fp, exponent: openarray[byte]) = +func pow*(a: var Fp, exponent: openarray[byte]) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer in canonical big endian representation @@ -204,7 +237,7 @@ func pow*(a: var Fp, exponent: openarray[byte]) = Fp.C.canUseNoCarryMontySquare() ) -func powUnsafeExponent*(a: var Fp, exponent: BigInt) = +func powUnsafeExponent*(a: var Fp, exponent: BigInt) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer @@ -224,7 +257,7 @@ func powUnsafeExponent*(a: var Fp, exponent: BigInt) = Fp.C.canUseNoCarryMontySquare() ) -func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) = +func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer a big integer in canonical big endian representation @@ -250,7 +283,7 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) = # # ############################################################ -func isSquare*[C](a: Fp[C]): SecretBool = +func isSquare*[C](a: Fp[C]): SecretBool {.inline.} = ## Returns true if ``a`` is a square (quadratic residue) in 𝔽p ## ## Assumes that the prime modulus ``p`` is public. @@ -272,7 +305,7 @@ func isSquare*[C](a: Fp[C]): SecretBool = xi.mres == C.getMontyPrimeMinus1() ) -func sqrt_p3mod4[C](a: var Fp[C]) = +func sqrt_p3mod4[C](a: var Fp[C]) {.inline.} = ## Compute the square root of ``a`` ## ## This requires ``a`` to be a square @@ -286,7 +319,7 @@ func sqrt_p3mod4[C](a: var Fp[C]) = static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3 a.powUnsafeExponent(C.getPrimePlus1div4_BE()) -func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) = +func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} = ## If ``a`` is a square, compute the square root of ``a`` in sqrt ## and the inverse square root of a in invsqrt ## @@ -307,7 +340,7 @@ func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) = # √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p) sqrt.prod(invsqrt, a) -func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool = +func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} = ## If ``a`` is a square, compute the square root of ``a`` in sqrt ## and the inverse square root of a in invsqrt ## @@ -319,7 +352,7 @@ func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): Secre euler.prod(sqrt, invsqrt) result = not(euler.mres == C.getMontyPrimeMinus1()) -func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool = +func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool {.inline.} = ## If ``a`` is a square, compute the square root of ``a`` ## if not, ``a`` is unmodified. ## @@ -334,7 +367,7 @@ func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool = result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a) a.ccopy(sqrt, result) -func sqrt*[C](a: var Fp[C]) = +func sqrt*[C](a: var Fp[C]) {.inline.} = ## Compute the square root of ``a`` ## ## This requires ``a`` to be a square @@ -349,7 +382,7 @@ func sqrt*[C](a: var Fp[C]) = else: {.error: "Square root is only implemented for p ≡ 3 (mod 4)".} -func sqrt_if_square*[C](a: var Fp[C]): SecretBool = +func sqrt_if_square*[C](a: var Fp[C]): SecretBool {.inline.} = ## If ``a`` is a square, compute the square root of ``a`` ## if not, ``a`` is unmodified. ## @@ -361,7 +394,7 @@ func sqrt_if_square*[C](a: var Fp[C]): SecretBool = else: {.error: "Square root is only implemented for p ≡ 3 (mod 4)".} -func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) = +func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} = ## Compute the square root and inverse square root of ``a`` ## ## This requires ``a`` to be a square @@ -376,7 +409,7 @@ func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) = else: {.error: "Square root is only implemented for p ≡ 3 (mod 4)".} -func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool = +func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} = ## Compute the square root and ivnerse square root of ``a`` ## ## This returns true if ``a`` is square and sqrt/invsqrt contains the square root/inverse square root @@ -403,15 +436,15 @@ func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool # - Those that return a field element # - Those that internally allocate a temporary field element -func `+`*(a, b: Fp): Fp {.noInit.} = +func `+`*(a, b: Fp): Fp {.noInit, inline.} = ## Addition modulo p result.sum(a, b) -func `-`*(a, b: Fp): Fp {.noInit.} = +func `-`*(a, b: Fp): Fp {.noInit, inline.} = ## Substraction modulo p result.diff(a, b) -func `*`*(a, b: Fp): Fp {.noInit.} = +func `*`*(a, b: Fp): Fp {.noInit, inline.} = ## Multiplication modulo p ## ## It is recommended to assign with {.noInit.} @@ -419,11 +452,11 @@ func `*`*(a, b: Fp): Fp {.noInit.} = ## routine will zero init internally the result. result.prod(a, b) -func `*=`*(a: var Fp, b: Fp) = +func `*=`*(a: var Fp, b: Fp) {.inline.} = ## Multiplication modulo p a.prod(a, b) -func square*(a: var Fp) = +func square*(a: var Fp) {.inline.}= ## Squaring modulo p a.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) diff --git a/constantine/arithmetic/finite_fields_asm_mul_x86.nim b/constantine/arithmetic/finite_fields_asm_mul_x86.nim new file mode 100644 index 0000000..f943875 --- /dev/null +++ b/constantine/arithmetic/finite_fields_asm_mul_x86.nim @@ -0,0 +1,223 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/macros, + # Internal + ../config/common, + ../primitives, + ./limbs + +# ############################################################ +# +# Assembly implementation of finite fields +# +# ############################################################ + +# Note: We can refer to at most 30 registers in inline assembly +# and "InputOutput" registers count double +# They are nice to let the compiler deals with mov +# but too constraining so we move things ourselves. + +static: doAssert UseX86ASM + +# Necessary for the compiler to find enough registers (enabled at -O1) +{.localPassC:"-fomit-frame-pointer".} + +# Montgomery multiplication +# ------------------------------------------------------------ +# Fallback when no ADX and BMI2 support (MULX, ADCX, ADOX) + +proc finalSub*( + ctx: var Assembler_x86, + r: Operand or OperandArray, + t, M, scratch: OperandArray + ) = + ## Reduce `t` into `r` modulo `M` + let N = M.len + ctx.comment "Final substraction" + for i in 0 ..< N: + ctx.mov scratch[i], t[i] + if i == 0: + ctx.sub scratch[i], M[i] + else: + ctx.sbb scratch[i], M[i] + + # If we borrowed it means that we were smaller than + # the modulus and we don't need "scratch" + for i in 0 ..< N: + ctx.cmovnc t[i], scratch[i] + ctx.mov r[i], t[i] + +macro montMul_CIOS_nocarry_gen[N: static int](r_MM: var Limbs[N], a_MM, b_MM, M_MM: Limbs[N], m0ninv_MM: BaseType): untyped = + ## Generate an optimized Montgomery Multiplication kernel + ## using the CIOS method + ## + ## The multiplication and reduction are further merged in the same loop + ## + ## This requires the most significant word of the Modulus + ## M[^1] < high(SecretWord) shr 2 (i.e. less than 0b00111...1111) + ## https://hackmd.io/@zkteam/modular_multiplication + + result = newStmtList() + + var ctx = init(Assembler_x86, BaseType) + let + scratchSlots = max(N, 6) + + # We could force M as immediate by specializing per moduli + M = init(OperandArray, nimSymbol = M_MM, N, PointerInReg, Input) + # If N is too big, we need to spill registers. TODO. + t = init(OperandArray, nimSymbol = ident"t", N, ElemsInReg, Output_EarlyClobber) + # MultiPurpose Register slots + scratch = init(OperandArray, nimSymbol = ident"scratch", scratchSlots, ElemsInReg, InputOutput_EnsureClobber) + + # MUL requires RAX and RDX + rRAX = Operand( + desc: OperandDesc( + asmId: "[rax]", + nimSymbol: ident"rax", + rm: RAX, + constraint: Output_EarlyClobber, + cEmit: "rax" + ) + ) + + rRDX = Operand( + desc: OperandDesc( + asmId: "[rdx]", + nimSymbol: ident"rdx", + rm: RDX, + constraint: Output_EarlyClobber, + cEmit: "rdx" + ) + ) + + m0ninv = Operand( + desc: OperandDesc( + asmId: "[m0ninv]", + nimSymbol: m0ninv_MM, + rm: MemOffsettable, + constraint: Input, + cEmit: "&" & $m0ninv_MM + ) + ) + + # We're really constrained by register and somehow setting as memory doesn't help + # So we store the result `r` in the scratch space and then reload it in RDX + # before the scratchspace is used in final substraction + a = scratch[0].asArrayAddr(len = N) # Store the `a` operand + b = scratch[1].asArrayAddr(len = N) # Store the `b` operand + A = scratch[2] # High part of extended precision multiplication + C = scratch[3] + m = scratch[4] # Stores (t[0] * m0ninv) mod 2^w + r = scratch[5] # Stores the `r` operand + + # Registers used: + # - 1 for `M` + # - 6 for `t` (at most) + # - 6 for `scratch` + # - 2 for RAX and RDX + # Total 15 out of 16 + # We can save 1 by hardcoding M as immediate (and m0ninv) + # but this prevent reusing the same code for multiple curves like BLS12-377 and BLS12-381 + # We might be able to save registers by having `r` and `M` be memory operand as well + + let tsym = t.nimSymbol + let scratchSym = scratch.nimSymbol + let eax = rRAX.desc.nimSymbol + let edx = rRDX.desc.nimSymbol + result.add quote do: + static: doAssert: sizeof(SecretWord) == sizeof(ByteAddress) + + var `tsym`: typeof(`r_MM`) # zero init + # Assumes 64-bit limbs on 64-bit arch (or you can't store an address) + var `scratchSym` {.noInit.}: Limbs[`scratchSlots`] + var `eax`{.noInit.}, `edx`{.noInit.}: BaseType + + `scratchSym`[0] = cast[SecretWord](`a_MM`[0].unsafeAddr) + `scratchSym`[1] = cast[SecretWord](`b_MM`[0].unsafeAddr) + `scratchSym`[5] = cast[SecretWord](`r_MM`[0].unsafeAddr) + + # Algorithm + # ----------------------------------------- + # for i=0 to N-1 + # (A, t[0]) <- a[0] * b[i] + t[0] + # m <- (t[0] * m0ninv) mod 2^w + # (C, _) <- m * M[0] + t[0] + # for j=1 to N-1 + # (A, t[j]) <- a[j] * b[i] + A + t[j] + # (C, t[j-1]) <- m * M[j] + C + t[j] + # + # t[N-1] = C + A + + # No register spilling handling + doAssert N <= 6, "The Assembly-optimized montgomery multiplication requires at most 6 limbs." + + for i in 0 ..< N: + # (A, t[0]) <- a[0] * b[i] + t[0] + ctx.mov rRAX, a[0] + ctx.mul rdx, rax, b[i], rax + if i == 0: # overwrite t[0] + ctx.mov t[0], rRAX + else: # Accumulate in t[0] + ctx.add t[0], rRAX + ctx.adc rRDX, 0 + ctx.mov A, rRDX + + # m <- (t[0] * m0ninv) mod 2^w + ctx.mov m, m0ninv + ctx.imul m, t[0] + + # (C, _) <- m * M[0] + t[0] + ctx.`xor` C, C + ctx.mov rRAX, M[0] + ctx.mul rdx, rax, m, rax + ctx.add rRAX, t[0] + ctx.adc C, rRDX + + for j in 1 ..< N: + # (A, t[j]) <- a[j] * b[i] + A + t[j] + ctx.mov rRAX, a[j] + ctx.mul rdx, rax, b[i], rax + if i == 0: + ctx.mov t[j], A + else: + ctx.add t[j], A + ctx.adc rRDX, 0 + ctx.`xor` A, A + ctx.add t[j], rRAX + ctx.adc A, rRDX + + # (C, t[j-1]) <- m * M[j] + C + t[j] + ctx.mov rRAX, M[j] + ctx.mul rdx, rax, m, rax + ctx.add C, t[j] + ctx.adc rRDX, 0 + ctx.add C, rRAX + ctx.adc rRDX, 0 + ctx.mov t[j-1], C + ctx.mov C, rRDX + + ctx.add A, C + ctx.mov t[N-1], A + + ctx.mov rRDX, r + let r2 = rRDX.asArrayAddr(len = N) + + ctx.finalSub( + r2, t, M, + scratch + ) + + result.add ctx.generate + +func montMul_CIOS_nocarry_asm*(r: var Limbs, a, b, M: Limbs, m0ninv: BaseType) = + ## Constant-time modular multiplication + montMul_CIOS_nocarry_gen(r, a, b, M, m0ninv) diff --git a/constantine/arithmetic/finite_fields_asm_mul_x86_adx_bmi2.nim b/constantine/arithmetic/finite_fields_asm_mul_x86_adx_bmi2.nim new file mode 100644 index 0000000..8c41e23 --- /dev/null +++ b/constantine/arithmetic/finite_fields_asm_mul_x86_adx_bmi2.nim @@ -0,0 +1,282 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/macros, + # Internal + ../config/common, + ../primitives, + ./limbs, + ./finite_fields_asm_mul_x86 + +# ############################################################ +# +# Assembly implementation of finite fields +# +# ############################################################ + +# Note: We can refer to at most 30 registers in inline assembly +# and "InputOutput" registers count double +# They are nice to let the compiler deals with mov +# but too constraining so we move things ourselves. + +static: doAssert UseX86ASM + +# MULX/ADCX/ADOX +{.localPassC:"-madx -mbmi2".} +# Necessary for the compiler to find enough registers (enabled at -O1) +{.localPassC:"-fomit-frame-pointer".} + +# Montgomery Multiplication +# ------------------------------------------------------------ +proc mulx_by_word( + ctx: var Assembler_x86, + C: Operand, + t: OperandArray, + a: Operand, # Pointer in scratchspace + word: Operand, + S, rRDX: Operand + ) = + ## Multiply the `a[0..= 2, "The Assembly-optimized montgomery multiplication requires at least 2 limbs." + ctx.comment " Outer loop i = 0" + ctx.`xor` rRDX, rRDX # Clear flags - TODO: necessary? + ctx.mov rRDX, word + + # for j=0 to N-1 + # (C,t[j]) := t[j] + a[j]*b[i] + C + + # First limb + ctx.mulx t[1], t[0], a[0], rdx + + # Steady state + for j in 1 ..< N-1: + ctx.mulx t[j+1], S, a[j], rdx + ctx.adox t[j], S # TODO, we probably can use ADC here + + # Last limb + ctx.mulx C, S, a[N-1], rdx + ctx.adox t[N-1], S + + # Final carries + ctx.comment " Mul carries i = 0" + ctx.mov rRDX, 0 # Set to 0 without clearing flags + ctx.adcx C, rRDX + ctx.adox C, rRDX + +proc mulaccx_by_word( + ctx: var Assembler_x86, + C: Operand, + t: OperandArray, + a: Operand, # Pointer in scratchspace + i: int, + word: Operand, + S, rRDX: Operand + ) = + ## Multiply the `a[0..= 2, "The Assembly-optimized montgomery multiplication requires at least 2 limbs." + doAssert i != 0 + + ctx.comment " Outer loop i = " & $i + ctx.`xor` rRDX, rRDX # Clear flags - TODO: necessary? + ctx.mov rRDX, word + + # for j=0 to N-1 + # (C,t[j]) := t[j] + a[j]*b[i] + C + + # Steady state + for j in 0 ..< N-1: + ctx.mulx C, S, a[j], rdx + ctx.adox t[j], S + ctx.adcx t[j+1], C + + # Last limb + ctx.mulx C, S, a[N-1], rdx + ctx.adox t[N-1], S + + # Final carries + ctx.comment " Mul carries i = " & $i + ctx.mov rRDX, 0 # Set to 0 without clearing flags + ctx.adcx C, rRDX + ctx.adox C, rRDX + +proc partialRedx( + ctx: var Assembler_x86, + C: Operand, + t: OperandArray, + M: OperandArray, + m0ninv: Operand, + lo, S, rRDX: Operand + ) = + ## Partial Montgomery reduction + ## For CIOS method + ## `C` the update carry flag (represents t[N]) + ## `t[0.. **every code compiled in 32-bit with MSVC on 64-bit architectures will call llmul every time a 64-bit multiplication is executed.** +- [When Constant-Time Source Yields Variable-Time Binary: Exploiting Curve25519-donna Built with MSVC 2015.](https://infoscience.epfl.ch/record/223794/files/32_1.pdf) + +#### Verification of Assembly + +The assembly code generated needs special tooling for formal verification that is different from the C code in https://github.com/mratsim/constantine/issues/6. +Recently Microsoft Research introduced Vale: +- Vale: Verifying High-Performance Cryptographic Assembly Code\ + Barry Bond and Chris Hawblitzel, Microsoft Research; Manos Kapritsos, University of Michigan; K. Rustan M. Leino and Jacob R. Lorch, Microsoft Research; Bryan Parno, Carnegie Mellon University; Ashay Rane, The University of Texas at Austin;Srinath Setty, Microsoft Research; Laure Thompson, Cornell University\ + https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-bond.pdf + https://github.com/project-everest/vale +Vale can be used to verify assembly crypto code against the architecture and also detect timing attacks. + +### Assembly Performance + +Beyond security, compilers do not expose several primitives that are necessary for necessary for multiprecision arithmetic. + +#### Add with carry, sub with borrow + +The most egregious example is add with carry which led to the GMP team to implement everything in Assembly even though this is a most basic need and almost all processor have an ADC instruction, some like the 6502 from 30 years ago only have ADC and no ADD. +See: +- https://gmplib.org/manual/Assembly-Carry-Propagation.html +- +![image](https://user-images.githubusercontent.com/22738317/83965806-8f4e2980-a8b6-11ea-9fbb-719e42d119dc.png) + +Some specific platforms might expose add with carry, for example x86 but even then the code generation might be extremely poor: https://gcc.godbolt.org/z/2h768y ```C #include #include @@ -47,7 +84,6 @@ void add256(uint64_t a[4], uint64_t b[4]){ carry = _addcarry_u64(carry, a[i], b[i], &a[i]); } ``` - GCC ```asm add256: @@ -70,7 +106,6 @@ add256: adcq %rax, 24(%rdi) ret ``` - Clang ```asm add256: @@ -84,8 +119,9 @@ add256: adcq %rax, 24(%rdi) retq ``` +(Reported fixed but it is not? https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67317) -### Inline assembly +And no way to use ADC for ARM architectures with GCC. +Clang does offer `__builtin_addcll` which might work now or [not](https://stackoverflow.com/questions/33690791/producing-good-add-with-carry-code-from-clang) as fixing the add with carry for x86 took years. Alternatively Clang does offer new arbitrary width integer since a month ago, called ExtInt http://blog.llvm.org/2020/04/the-new-clang-extint-feature-provides.html it is unknown however if code is guaranted to be constant-time. -Using inline assembly will sacrifice code readability, portability, auditability and maintainability. -That said the performance might be worth it. +See also: https://stackoverflow.com/questions/29029572/multi-word-addition-using-the-carry-flag/29212615 diff --git a/constantine/primitives/cpuinfo_x86.nim b/constantine/primitives/cpuinfo_x86.nim new file mode 100644 index 0000000..35a1a6d --- /dev/null +++ b/constantine/primitives/cpuinfo_x86.nim @@ -0,0 +1,779 @@ +# From awr1: https://github.com/nim-lang/Nim/pull/11816/files + +proc cpuidX86(eaxi, ecxi: int32): tuple[eax, ebx, ecx, edx: int32] {.used.}= + when defined(vcc): + # limited inline asm support in vcc, so intrinsics, here we go: + proc cpuidVcc(cpuInfo: ptr int32; functionID, subFunctionID: int32) + {.cdecl, importc: "__cpuidex", header: "intrin.h".} + cpuidVcc(addr result.eax, eaxi, ecxi) + else: + var (eaxr, ebxr, ecxr, edxr) = (0'i32, 0'i32, 0'i32, 0'i32) + asm """ + cpuid + :"=a"(`eaxr`), "=b"(`ebxr`), "=c"(`ecxr`), "=d"(`edxr`) + :"a"(`eaxi`), "c"(`ecxi`)""" + (eaxr, ebxr, ecxr, edxr) + +proc cpuNameX86(): string {.used.}= + var leaves {.global.} = cast[array[48, char]]([ + cpuidX86(eaxi = 0x80000002'i32, ecxi = 0), + cpuidX86(eaxi = 0x80000003'i32, ecxi = 0), + cpuidX86(eaxi = 0x80000004'i32, ecxi = 0)]) + result = $cast[cstring](addr leaves[0]) + +type + X86Feature {.pure.} = enum + HypervisorPresence, Hyperthreading, NoSMT, IntelVtx, Amdv, X87fpu, Mmx, + MmxExt, F3DNow, F3DNowEnhanced, Prefetch, Sse, Sse2, Sse3, Ssse3, Sse4a, + Sse41, Sse42, Avx, Avx2, Avx512f, Avx512dq, Avx512ifma, Avx512pf, + Avx512er, Avx512cd, Avx512bw, Avx512vl, Avx512vbmi, Avx512vbmi2, + Avx512vpopcntdq, Avx512vnni, Avx512vnniw4, Avx512fmaps4, Avx512bitalg, + Avx512bfloat16, Avx512vp2intersect, Rdrand, Rdseed, MovBigEndian, Popcnt, + Fma3, Fma4, Xop, Cas8B, Cas16B, Abm, Bmi1, Bmi2, TsxHle, TsxRtm, Adx, Sgx, + Gfni, Aes, Vaes, Vpclmulqdq, Pclmulqdq, NxBit, Float16c, Sha, Clflush, + ClflushOpt, Clwb, PrefetchWT1, Mpx + +let + leaf1 = cpuidX86(eaxi = 1, ecxi = 0) + leaf7 = cpuidX86(eaxi = 7, ecxi = 0) + leaf8 = cpuidX86(eaxi = 0x80000001'i32, ecxi = 0) + +# The reason why we don't just evaluate these directly in the `let` variable +# list is so that we can internally organize features by their input (leaf) +# and output registers. +proc testX86Feature(feature: X86Feature): bool = + proc test(input, bit: int): bool = + ((1 shl bit) and input) != 0 + + # see: https://en.wikipedia.org/wiki/CPUID#Calling_CPUID + # see: Intel® Architecture Instruction Set Extensions and Future Features + # Programming Reference + result = case feature + # leaf 1, edx + of X87fpu: + leaf1.edx.test(0) + of Clflush: + leaf1.edx.test(19) + of Mmx: + leaf1.edx.test(23) + of Sse: + leaf1.edx.test(25) + of Sse2: + leaf1.edx.test(26) + of Hyperthreading: + leaf1.edx.test(28) + + # leaf 1, ecx + of Sse3: + leaf1.ecx.test(0) + of Pclmulqdq: + leaf1.ecx.test(1) + of IntelVtx: + leaf1.ecx.test(5) + of Ssse3: + leaf1.ecx.test(9) + of Fma3: + leaf1.ecx.test(12) + of Cas16B: + leaf1.ecx.test(13) + of Sse41: + leaf1.ecx.test(19) + of Sse42: + leaf1.ecx.test(20) + of MovBigEndian: + leaf1.ecx.test(22) + of Popcnt: + leaf1.ecx.test(23) + of Aes: + leaf1.ecx.test(25) + of Avx: + leaf1.ecx.test(28) + of Float16c: + leaf1.ecx.test(29) + of Rdrand: + leaf1.ecx.test(30) + of HypervisorPresence: + leaf1.ecx.test(31) + + # leaf 7, ecx + of PrefetchWT1: + leaf7.ecx.test(0) + of Avx512vbmi: + leaf7.ecx.test(1) + of Avx512vbmi2: + leaf7.ecx.test(6) + of Gfni: + leaf7.ecx.test(8) + of Vaes: + leaf7.ecx.test(9) + of Vpclmulqdq: + leaf7.ecx.test(10) + of Avx512vnni: + leaf7.ecx.test(11) + of Avx512bitalg: + leaf7.ecx.test(12) + of Avx512vpopcntdq: + leaf7.ecx.test(14) + + # lead 7, eax + of Avx512bfloat16: + leaf7.eax.test(5) + + # leaf 7, ebx + of Sgx: + leaf7.ebx.test(2) + of Bmi1: + leaf7.ebx.test(3) + of TsxHle: + leaf7.ebx.test(4) + of Avx2: + leaf7.ebx.test(5) + of Bmi2: + leaf7.ebx.test(8) + of TsxRtm: + leaf7.ebx.test(11) + of Mpx: + leaf7.ebx.test(14) + of Avx512f: + leaf7.ebx.test(16) + of Avx512dq: + leaf7.ebx.test(17) + of Rdseed: + leaf7.ebx.test(18) + of Adx: + leaf7.ebx.test(19) + of Avx512ifma: + leaf7.ebx.test(21) + of ClflushOpt: + leaf7.ebx.test(23) + of Clwb: + leaf7.ebx.test(24) + of Avx512pf: + leaf7.ebx.test(26) + of Avx512er: + leaf7.ebx.test(27) + of Avx512cd: + leaf7.ebx.test(28) + of Sha: + leaf7.ebx.test(29) + of Avx512bw: + leaf7.ebx.test(30) + of Avx512vl: + leaf7.ebx.test(31) + + # leaf 7, edx + of Avx512vnniw4: + leaf7.edx.test(2) + of Avx512fmaps4: + leaf7.edx.test(3) + of Avx512vp2intersect: + leaf7.edx.test(8) + + # leaf 8, edx + of NoSMT: + leaf8.edx.test(1) + of Cas8B: + leaf8.edx.test(8) + of NxBit: + leaf8.edx.test(20) + of MmxExt: + leaf8.edx.test(22) + of F3DNowEnhanced: + leaf8.edx.test(30) + of F3DNow: + leaf8.edx.test(31) + + # leaf 8, ecx + of Amdv: + leaf8.ecx.test(2) + of Abm: + leaf8.ecx.test(5) + of Sse4a: + leaf8.ecx.test(6) + of Prefetch: + leaf8.ecx.test(8) + of Xop: + leaf8.ecx.test(11) + of Fma4: + leaf8.ecx.test(16) + +let + isHypervisorPresentImpl = testX86Feature(HypervisorPresence) + hasSimultaneousMultithreadingImpl = + testX86Feature(Hyperthreading) or not testX86Feature(NoSMT) + hasIntelVtxImpl = testX86Feature(IntelVtx) + hasAmdvImpl = testX86Feature(Amdv) + hasX87fpuImpl = testX86Feature(X87fpu) + hasMmxImpl = testX86Feature(Mmx) + hasMmxExtImpl = testX86Feature(MmxExt) + has3DNowImpl = testX86Feature(F3DNow) + has3DNowEnhancedImpl = testX86Feature(F3DNowEnhanced) + hasPrefetchImpl = testX86Feature(Prefetch) or testX86Feature(F3DNow) + hasSseImpl = testX86Feature(Sse) + hasSse2Impl = testX86Feature(Sse2) + hasSse3Impl = testX86Feature(Sse3) + hasSsse3Impl = testX86Feature(Ssse3) + hasSse4aImpl = testX86Feature(Sse4a) + hasSse41Impl = testX86Feature(Sse41) + hasSse42Impl = testX86Feature(Sse42) + hasAvxImpl = testX86Feature(Avx) + hasAvx2Impl = testX86Feature(Avx2) + hasAvx512fImpl = testX86Feature(Avx512f) + hasAvx512dqImpl = testX86Feature(Avx512dq) + hasAvx512ifmaImpl = testX86Feature(Avx512ifma) + hasAvx512pfImpl = testX86Feature(Avx512pf) + hasAvx512erImpl = testX86Feature(Avx512er) + hasAvx512cdImpl = testX86Feature(Avx512dq) + hasAvx512bwImpl = testX86Feature(Avx512bw) + hasAvx512vlImpl = testX86Feature(Avx512vl) + hasAvx512vbmiImpl = testX86Feature(Avx512vbmi) + hasAvx512vbmi2Impl = testX86Feature(Avx512vbmi2) + hasAvx512vpopcntdqImpl = testX86Feature(Avx512vpopcntdq) + hasAvx512vnniImpl = testX86Feature(Avx512vnni) + hasAvx512vnniw4Impl = testX86Feature(Avx512vnniw4) + hasAvx512fmaps4Impl = testX86Feature(Avx512fmaps4) + hasAvx512bitalgImpl = testX86Feature(Avx512bitalg) + hasAvx512bfloat16Impl = testX86Feature(Avx512bfloat16) + hasAvx512vp2intersectImpl = testX86Feature(Avx512vp2intersect) + hasRdrandImpl = testX86Feature(Rdrand) + hasRdseedImpl = testX86Feature(Rdseed) + hasMovBigEndianImpl = testX86Feature(MovBigEndian) + hasPopcntImpl = testX86Feature(Popcnt) + hasFma3Impl = testX86Feature(Fma3) + hasFma4Impl = testX86Feature(Fma4) + hasXopImpl = testX86Feature(Xop) + hasCas8BImpl = testX86Feature(Cas8B) + hasCas16BImpl = testX86Feature(Cas16B) + hasAbmImpl = testX86Feature(Abm) + hasBmi1Impl = testX86Feature(Bmi1) + hasBmi2Impl = testX86Feature(Bmi2) + hasTsxHleImpl = testX86Feature(TsxHle) + hasTsxRtmImpl = testX86Feature(TsxRtm) + hasAdxImpl = testX86Feature(TsxHle) + hasSgxImpl = testX86Feature(Sgx) + hasGfniImpl = testX86Feature(Gfni) + hasAesImpl = testX86Feature(Aes) + hasVaesImpl = testX86Feature(Vaes) + hasVpclmulqdqImpl = testX86Feature(Vpclmulqdq) + hasPclmulqdqImpl = testX86Feature(Pclmulqdq) + hasNxBitImpl = testX86Feature(NxBit) + hasFloat16cImpl = testX86Feature(Float16c) + hasShaImpl = testX86Feature(Sha) + hasClflushImpl = testX86Feature(Clflush) + hasClflushOptImpl = testX86Feature(ClflushOpt) + hasClwbImpl = testX86Feature(Clwb) + hasPrefetchWT1Impl = testX86Feature(PrefetchWT1) + hasMpxImpl = testX86Feature(Mpx) + +# NOTE: We use procedures here (layered over the variables) to keep the API +# consistent and usable against possible future heterogenous systems with ISA +# differences between cores (a possibility that has historical precedents, for +# instance, the PPU/SPU relationship found on the IBM Cell). If future systems +# do end up having disparate ISA features across multiple cores, expect there to +# be a "cpuCore" argument added to the feature procs. + +proc isHypervisorPresent*(): bool {.inline.} = + return isHypervisorPresentImpl + ## **(x86 Only)** + ## + ## Reports `true` if this application is running inside of a virtual machine + ## (this is by no means foolproof). + +proc hasSimultaneousMultithreading*(): bool {.inline.} = + return hasSimultaneousMultithreadingImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware is utilizing simultaneous multithreading + ## (branded as *"hyperthreads"* on Intel processors). + +proc hasIntelVtx*(): bool {.inline.} = + return hasIntelVtxImpl + ## **(x86 Only)** + ## + ## Reports `true` if the Intel virtualization extensions (VT-x) are available. + +proc hasAmdv*(): bool {.inline.} = + return hasAmdvImpl + ## **(x86 Only)** + ## + ## Reports `true` if the AMD virtualization extensions (AMD-V) are available. + +proc hasX87fpu*(): bool {.inline.} = + return hasX87fpuImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use x87 floating-point instructions + ## (includes support for single, double, and 80-bit percision floats as per + ## IEEE 754-1985). + ## + ## By virtue of SSE2 enforced compliance on AMD64 CPUs, this should always be + ## `true` on 64-bit x86 processors. It should be noted that support of these + ## instructions is deprecated on 64-bit versions of Windows - see MSDN_. + ## + ## .. _MSDN: https://docs.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms + +proc hasMmx*(): bool {.inline.} = + return hasMmxImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use MMX SIMD instructions. + ## + ## By virtue of SSE2 enforced compliance on AMD64 CPUs, this should always be + ## `true` on 64-bit x86 processors. It should be noted that support of these + ## instructions is deprecated on 64-bit versions of Windows (see MSDN_ for + ## more info). + ## + ## .. _MSDN: https://docs.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms + +proc hasMmxExt*(): bool {.inline.} = + return hasMmxExtImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use "Extended MMX" SIMD instructions. + ## + ## It should be noted that support of these instructions is deprecated on + ## 64-bit versions of Windows (see MSDN_ for more info). + ## + ## .. _MSDN: https://docs.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms + +proc has3DNow*(): bool {.inline.} = + return has3DNowImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use 3DNow! SIMD instructions. + ## + ## It should be noted that support of these instructions is deprecated on + ## 64-bit versions of Windows (see MSDN_ for more info), and that the 3DNow! + ## instructions (with an exception made for the prefetch instructions, see the + ## `hasPrefetch` procedure) have been phased out of AMD processors since 2010 + ## (see `AMD Developer Central`_ for more info). + ## + ## .. _MSDN: https://docs.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms + ## .. _`AMD Developer Central`: https://web.archive.org/web/20131109151245/http://developer.amd.com/community/blog/2010/08/18/3dnow-deprecated/ + +proc has3DNowEnhanced*(): bool {.inline.} = + return has3DNowEnhancedImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use "Enhanced 3DNow!" SIMD instructions. + ## + ## It should be noted that support of these instructions is deprecated on + ## 64-bit versions of Windows (see MSDN_ for more info), and that the 3DNow! + ## instructions (with an exception made for the prefetch instructions, see the + ## `hasPrefetch` procedure) have been phased out of AMD processors since 2010 + ## (see `AMD Developer Central`_ for more info). + ## + ## .. _MSDN: https://docs.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms + ## .. _`AMD Developer Central`: https://web.archive.org/web/20131109151245/http://developer.amd.com/community/blog/2010/08/18/3dnow-deprecated/ + +proc hasPrefetch*(): bool {.inline.} = + return hasPrefetchImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use the `PREFETCH` and `PREFETCHW` + ## instructions. These instructions originally included as part of 3DNow!, but + ## potentially indepdendent from the rest of it due to changes in contemporary + ## AMD processors (see above). + +proc hasSse*(): bool {.inline.} = + return hasSseImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use the SSE (Streaming SIMD Extensions) + ## 1.0 instructions, which introduced 128-bit SIMD on x86 machines. + ## + ## By virtue of SSE2 enforced compliance on AMD64 CPUs, this should always be + ## `true` on 64-bit x86 processors. + +proc hasSse2*(): bool {.inline.} = + return hasSse2Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use the SSE (Streaming SIMD Extensions) + ## 2.0 instructions. + ## + ## By virtue of SSE2 enforced compliance on AMD64 CPUs, this should always be + ## `true` on 64-bit x86 processors. + +proc hasSse3*(): bool {.inline.} = + return hasSse3Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use SSE (Streaming SIMD Extensions) 3.0 + ## instructions. + +proc hasSsse3*(): bool {.inline.} = + return hasSsse3Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use Supplemental SSE (Streaming SIMD + ## Extensions) 3.0 instructions. + +proc hasSse4a*(): bool {.inline.} = + return hasSse4aImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use Supplemental SSE (Streaming SIMD + ## Extensions) 4a instructions. + +proc hasSse41*(): bool {.inline.} = + return hasSse41Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use Supplemental SSE (Streaming SIMD + ## Extensions) 4.1 instructions. + +proc hasSse42*(): bool {.inline.} = + return hasSse42Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use Supplemental SSE (Streaming SIMD + ## Extensions) 4.2 instructions. + +proc hasAvx*(): bool {.inline.} = + return hasAvxImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 1.0 instructions, which introduced 256-bit SIMD on x86 machines along with + ## addded reencoded versions of prior 128-bit SSE instructions into the more + ## code-dense and non-backward compatible VEX (Vector Extensions) format. + +proc hasAvx2*(): bool {.inline.} = + return hasAvx2Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) 2.0 + ## instructions. + +proc hasAvx512f*(): bool {.inline.} = + return hasAvx512fImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit F (Foundation) instructions. + +proc hasAvx512dq*(): bool {.inline.} = + return hasAvx512dqImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit DQ (Doubleword + Quadword) instructions. + +proc hasAvx512ifma*(): bool {.inline.} = + return hasAvx512ifmaImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit IFMA (Integer Fused Multiply Accumulation) instructions. + +proc hasAvx512pf*(): bool {.inline.} = + return hasAvx512pfImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit PF (Prefetch) instructions. + +proc hasAvx512er*(): bool {.inline.} = + return hasAvx512erImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit ER (Exponential and Reciprocal) instructions. + +proc hasAvx512cd*(): bool {.inline.} = + return hasAvx512cdImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit CD (Conflict Detection) instructions. + +proc hasAvx512bw*(): bool {.inline.} = + return hasAvx512bwImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit BW (Byte and Word) instructions. + +proc hasAvx512vl*(): bool {.inline.} = + return hasAvx512vlImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit VL (Vector Length) instructions. + +proc hasAvx512vbmi*(): bool {.inline.} = + return hasAvx512vbmiImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit VBMI (Vector Byte Manipulation) 1.0 instructions. + +proc hasAvx512vbmi2*(): bool {.inline.} = + return hasAvx512vbmi2Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit VBMI (Vector Byte Manipulation) 2.0 instructions. + +proc hasAvx512vpopcntdq*(): bool {.inline.} = + return hasAvx512vpopcntdqImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use the AVX (Advanced Vector Extensions) + ## 512-bit `VPOPCNTDQ` (population count, i.e. determine number of flipped + ## bits) instruction. + +proc hasAvx512vnni*(): bool {.inline.} = + return hasAvx512vnniImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit VNNI (Vector Neural Network) instructions. + +proc hasAvx512vnniw4*(): bool {.inline.} = + return hasAvx512vnniw4Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit 4VNNIW (Vector Neural Network Word Variable Percision) + ## instructions. + +proc hasAvx512fmaps4*(): bool {.inline.} = + return hasAvx512fmaps4Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit 4FMAPS (Fused-Multiply-Accumulation Single-percision) instructions. + +proc hasAvx512bitalg*(): bool {.inline.} = + return hasAvx512bitalgImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit BITALG (Bit Algorithms) instructions. + +proc hasAvx512bfloat16*(): bool {.inline.} = + return hasAvx512bfloat16Impl + ## **(x86 Only)** + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit BFLOAT16 (8-bit exponent, 7-bit mantissa) instructions used by + ## Intel DL (Deep Learning) Boost. + +proc hasAvx512vp2intersect*(): bool {.inline.} = + return hasAvx512vp2intersectImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware can use AVX (Advanced Vector Extensions) + ## 512-bit VP2INTERSECT (Compute Intersections between Dualwords + Quadwords) + ## instructions. + +proc hasRdrand*(): bool {.inline.} = + return hasRdrandImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `RDRAND` instruction, + ## i.e. Intel on-CPU hardware random number generation. + +proc hasRdseed*(): bool {.inline.} = + return hasRdseedImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `RDSEED` instruction, + ## i.e. Intel on-CPU hardware random number generation (used for seeding other + ## PRNGs). + +proc hasMovBigEndian*(): bool {.inline.} = + return hasMovBigEndianImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `MOVBE` instruction for + ## endianness/byte-order switching. + +proc hasPopcnt*(): bool {.inline.} = + return hasPopcntImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `POPCNT` (population + ## count, i.e. determine number of flipped bits) instruction. + +proc hasFma3*(): bool {.inline.} = + return hasFma3Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the FMA3 (Fused Multiply + ## Accumulation 3-operand) SIMD instructions. + +proc hasFma4*(): bool {.inline.} = + return hasFma4Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the FMA4 (Fused Multiply + ## Accumulation 4-operand) SIMD instructions. + +proc hasXop*(): bool {.inline.} = + return hasXopImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the XOP (eXtended + ## Operations) SIMD instructions. These instructions are exclusive to the + ## Bulldozer AMD microarchitecture family (i.e. Bulldozer, Piledriver, + ## Steamroller, and Excavator) and were phased out with the release of the Zen + ## design. + +proc hasCas8B*(): bool {.inline.} = + return hasCas8BImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the (`LOCK`-able) + ## `CMPXCHG8B` 64-bit compare-and-swap instruction. + +proc hasCas16B*(): bool {.inline.} = + return hasCas16BImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the (`LOCK`-able) + ## `CMPXCHG16B` 128-bit compare-and-swap instruction. + +proc hasAbm*(): bool {.inline.} = + return hasAbmImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for ABM (Advanced Bit + ## Manipulation) insturctions (i.e. `POPCNT` and `LZCNT` for counting leading + ## zeroes). + +proc hasBmi1*(): bool {.inline.} = + return hasBmi1Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for BMI (Bit Manipulation) 1.0 + ## instructions. + +proc hasBmi2*(): bool {.inline.} = + return hasBmi2Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for BMI (Bit Manipulation) 2.0 + ## instructions. + +proc hasTsxHle*(): bool {.inline.} = + return hasTsxHleImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for HLE (Hardware Lock Elision) + ## as part of Intel's TSX (Transactional Synchronization Extensions). + +proc hasTsxRtm*(): bool {.inline.} = + return hasTsxRtmImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for RTM (Restricted + ## Transactional Memory) as part of Intel's TSX (Transactional Synchronization + ## Extensions). + +proc hasAdx*(): bool {.inline.} = + return hasAdxImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for ADX (Multi-percision + ## Add-Carry Extensions) insructions. + +proc hasSgx*(): bool {.inline.} = + return hasSgxImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for SGX (Software Guard + ## eXtensions) memory encryption technology. + +proc hasGfni*(): bool {.inline.} = + return hasGfniImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for GFNI (Galois Field Affine + ## Transformation) instructions. + +proc hasAes*(): bool {.inline.} = + return hasAesImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for AESNI (Advanced Encryption + ## Standard) instructions. + +proc hasVaes*(): bool {.inline.} = + return hasVaesImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for VAES (Vectorized Advanced + ## Encryption Standard) instructions. + +proc hasVpclmulqdq*(): bool {.inline.} = + return hasVpclmulqdqImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for `VCLMULQDQ` (512 and 256-bit + ## Carryless Multiplication) instructions. + +proc hasPclmulqdq*(): bool {.inline.} = + return hasPclmulqdqImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for `PCLMULQDQ` (128-bit + ## Carryless Multiplication) instructions. + +proc hasNxBit*(): bool {.inline.} = + return hasNxBitImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for NX-bit (No-eXecute) + ## technology for marking pages of memory as non-executable. + +proc hasFloat16c*(): bool {.inline.} = + return hasFloat16cImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for F16C instructions, used for + ## converting 16-bit "half-percision" floating-point values to and from + ## single-percision floating-point values. + +proc hasSha*(): bool {.inline.} = + return hasShaImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for SHA (Secure Hash Algorithm) + ## instructions. + +proc hasClflush*(): bool {.inline.} = + return hasClflushImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `CLFLUSH` (Cache-line + ## Flush) instruction. + +proc hasClflushOpt*(): bool {.inline.} = + return hasClflushOptImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `CLFLUSHOPT` (Cache-line + ## Flush Optimized) instruction. + +proc hasClwb*(): bool {.inline.} = + return hasClwbImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `CLWB` (Cache-line Write + ## Back) instruction. + +proc hasPrefetchWT1*(): bool {.inline.} = + return hasPrefetchWT1Impl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for the `PREFECTHWT1` + ## instruction. + +proc hasMpx*(): bool {.inline.} = + return hasMpxImpl + ## **(x86 Only)** + ## + ## Reports `true` if the hardware has support for MPX (Memory Protection + ## eXtensions). diff --git a/constantine/primitives/macro_assembler_x86.nim b/constantine/primitives/macro_assembler_x86.nim new file mode 100644 index 0000000..f9630f7 --- /dev/null +++ b/constantine/primitives/macro_assembler_x86.nim @@ -0,0 +1,620 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import std/[macros, strutils, sets, hashes] + +# A compile-time inline assembler + +type + RM* = enum + ## Register or Memory operand + # https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html + Reg = "r" + Mem = "m" + AnyRegOrMem = "rm" # use "r, m" instead? + Imm = "i" + MemOffsettable = "o" + AnyRegMemImm = "g" + AnyMemOffImm = "oi" + AnyRegImm = "ri" + + PointerInReg = "r" # Store an array pointer + ElemsInReg = "r" # Store each individual array element in reg + + # Specific registers + RCX = "c" + RDX = "d" + R8 = "r8" + + RAX = "a" + + Register* = enum + rbx, rdx, r8, rax + + Constraint* = enum + ## GCC extended assembly modifier + Input = "" + Input_Commutative = "%" + Input_EarlyClobber = "&" + Output_Overwrite = "=" + Output_EarlyClobber = "=&" + InputOutput = "+" + InputOutput_EnsureClobber = "+&" # For register InputOutput, clang needs "+&" bug? + + OpKind = enum + kRegister + kFromArray + kArrayAddr + + Operand* = object + desc*: OperandDesc + case kind: OpKind + of kRegister: + discard + of kFromArray: + offset: int + of kArrayAddr: + buf: seq[Operand] + + OperandDesc* = ref object + asmId*: string # [a] - ASM id + nimSymbol*: NimNode # a - Nim nimSymbol + rm*: RM + constraint*: Constraint + cEmit*: string # C emit for example a->limbs + + OperandArray* = object + nimSymbol*: NimNode + buf: seq[Operand] + + OperandReuse* = object + # Allow reusing a register + asmId*: string + + Assembler_x86* = object + code: string + operands: HashSet[OperandDesc] + wordBitWidth*: int + wordSize: int + areFlagsClobbered: bool + isStackClobbered: bool + + Stack* = object + +const SpecificRegisters = {RCX, RDX, R8, RAX} +const OutputReg = {Output_EarlyClobber, InputOutput, InputOutput_EnsureClobber, Output_Overwrite} + +func hash(od: OperandDesc): Hash = + {.noSideEffect.}: + hash($od.nimSymbol) + +# TODO: remove the need of OperandArray + +func len*(opArray: OperandArray): int = + opArray.buf.len + +proc `[]`*(opArray: OperandArray, index: int): Operand = + opArray.buf[index] + +func `[]`*(opArray: var OperandArray, index: int): var Operand = + opArray.buf[index] + +func `[]`*(arrayAddr: Operand, index: int): Operand = + arrayAddr.buf[index] + +func `[]`*(arrayAddr: var Operand, index: int): var Operand = + arrayAddr.buf[index] + +func init*(T: type Assembler_x86, Word: typedesc[SomeUnsignedInt]): Assembler_x86 = + result.wordSize = sizeof(Word) + result.wordBitWidth = result.wordSize * 8 + +func init*(T: type OperandArray, nimSymbol: NimNode, len: int, rm: RM, constraint: Constraint): OperandArray = + doAssert rm in { + MemOffsettable, + AnyMemOffImm, + PointerInReg, + ElemsInReg + } or rm in SpecificRegisters + + result.buf.setLen(len) + + # We need to dereference the hidden pointer of var param + let isHiddenDeref = nimSymbol.kind == nnkHiddenDeref + let nimSymbol = if isHiddenDeref: nimSymbol[0] + else: nimSymbol + {.noSideEffect.}: + let symStr = $nimSymbol + + result.nimSymbol = nimSymbol + + if rm in {PointerInReg, MemOffsettable, AnyMemOffImm} or + rm in SpecificRegisters: + let desc = OperandDesc( + asmId: "[" & symStr & "]", + nimSymbol: nimSymbol, + rm: rm, + constraint: constraint, + cEmit: symStr + ) + for i in 0 ..< len: + result.buf[i] = Operand( + desc: desc, + kind: kFromArray, + offset: i + ) + else: + # We can't store an array in register so we create assign individual register + # per array elements instead + for i in 0 ..< len: + result.buf[i] = Operand( + desc: OperandDesc( + asmId: "[" & symStr & $i & "]", + nimSymbol: ident(symStr & $i), + rm: rm, + constraint: constraint, + cEmit: symStr & "[" & $i & "]" + ), + kind: kRegister + ) + +func asArrayAddr*(op: Operand, len: int): Operand = + ## Use the value stored in an operand as an array address + doAssert op.desc.rm in {Reg, PointerInReg, ElemsInReg}+SpecificRegisters + result = Operand( + kind: kArrayAddr, + desc: nil, + buf: newSeq[Operand](len) + ) + for i in 0 ..< len: + result.buf[i] = Operand( + desc: op.desc, + kind: kFromArray, + offset: i + ) + +# Code generation +# ------------------------------------------------------------------------------------------------------------ + +func generate*(a: Assembler_x86): NimNode = + ## Generate the inline assembly code from + ## the desired instruction + + var + outOperands: seq[string] + inOperands: seq[string] + memClobbered = false + + for odesc in a.operands.items(): + var decl: string + if odesc.rm in SpecificRegisters: + # [a] "rbx" (`a`) + decl = odesc.asmId & "\"" & $odesc.constraint & $odesc.rm & "\"" & + " (`" & odesc.cEmit & "`)" + elif odesc.rm in {Mem, AnyRegOrMem, MemOffsettable, AnyRegMemImm, AnyMemOffImm}: + # [a] "+r" (`*a`) + # We need to deref the pointer to memory + decl = odesc.asmId & " \"" & $odesc.constraint & $odesc.rm & "\"" & + " (`*" & odesc.cEmit & "`)" + else: + # [a] "+r" (`a[0]`) + decl = odesc.asmId & " \"" & $odesc.constraint & $odesc.rm & "\"" & + " (`" & odesc.cEmit & "`)" + + if odesc.constraint in {Input, Input_Commutative}: + inOperands.add decl + else: + outOperands.add decl + + if odesc.rm == PointerInReg and odesc.constraint in {Output_Overwrite, Output_EarlyClobber, InputOutput, InputOutput_EnsureClobber}: + memClobbered = true + + var params: string + params.add ": " & outOperands.join(", ") & '\n' + params.add ": " & inOperands.join(", ") & '\n' + + let clobbers = [(a.isStackClobbered, "sp"), + (a.areFlagsClobbered, "cc"), + (memClobbered, "memory")] + var clobberList = ": " + for (clobbered, str) in clobbers: + if clobbered: + if clobberList.len == 2: + clobberList.add "\"" & str & '\"' + else: + clobberList.add ", \"" & str & '\"' + + params.add clobberList + + # GCC will optimize ASM away if there are no + # memory operand or volatile + memory clobber + # https://stackoverflow.com/questions/34244185/looping-over-arrays-with-inline-assembly + + # result = nnkAsmStmt.newTree( + # newEmptyNode(), + # newLit(asmStmt & params) + # ) + + var asmStmt = "\"" & a.code.replace("\n", "\\n\"\n\"") + asmStmt.setLen(asmStmt.len - 1) # drop the last quote + + result = nnkPragma.newTree( + nnkExprColonExpr.newTree( + ident"emit", + newLit( + "asm volatile(\n" & asmStmt & params & ");" + ) + ) + ) + +func getStrOffset(a: Assembler_x86, op: Operand): string = + if op.kind != kFromArray: + return "%" & op.desc.asmId + + # Beware GCC / Clang differences with array offsets + # https://lists.llvm.org/pipermail/llvm-dev/2017-August/116202.html + + if op.desc.rm in {Mem, AnyRegOrMem, MemOffsettable, AnyMemOffImm, AnyRegMemImm}: + # Directly accessing memory + if op.offset == 0: + return "%" & op.desc.asmId + if defined(gcc): + return $(op.offset * a.wordSize) & "+%" & op.desc.asmId + elif defined(clang): + return $(op.offset * a.wordSize) & "%" & op.desc.asmId + else: + error "Unconfigured compiler" + elif op.desc.rm == PointerInReg or + op.desc.rm in SpecificRegisters or + (op.desc.rm == ElemsInReg and op.kind == kFromArray): + if op.offset == 0: + return "(%" & $op.desc.asmId & ')' + if defined(gcc): + return $(op.offset * a.wordSize) & "+(%" & $op.desc.asmId & ')' + elif defined(clang): + return $(op.offset * a.wordSize) & "(%" & $op.desc.asmId & ')' + else: + error "Unconfigured compiler" + else: + error "Unsupported: " & $op.desc.rm.ord + +func codeFragment(a: var Assembler_x86, instr: string, op: Operand) = + # Generate a code fragment + let off = a.getStrOffset(op) + + if a.wordBitWidth == 64: + a.code &= instr & "q " & off & '\n' + elif a.wordBitWidth == 32: + a.code &= instr & "l " & off & '\n' + else: + error "Unsupported bitwidth: " & $a.wordBitWidth + + a.operands.incl op.desc + +func codeFragment(a: var Assembler_x86, instr: string, op0, op1: Operand) = + # Generate a code fragment + # ⚠️ Warning: + # The caller should deal with destination/source operand + # so that it fits GNU Assembly + let off0 = a.getStrOffset(op0) + let off1 = a.getStrOffset(op1) + + if a.wordBitWidth == 64: + a.code &= instr & "q " & off0 & ", " & off1 & '\n' + elif a.wordBitWidth == 32: + a.code &= instr & "l " & off0 & ", " & off1 & '\n' + else: + error "Unsupported bitwidth: " & $a.wordBitWidth + + a.operands.incl op0.desc + a.operands.incl op1.desc + +func codeFragment(a: var Assembler_x86, instr: string, imm: int, op: Operand) = + # Generate a code fragment + # ⚠️ Warning: + # The caller should deal with destination/source operand + # so that it fits GNU Assembly + let off = a.getStrOffset(op) + + if a.wordBitWidth == 64: + a.code &= instr & "q $" & $imm & ", " & off & '\n' + else: + a.code &= instr & "l $" & $imm & ", " & off & '\n' + + a.operands.incl op.desc + +func codeFragment(a: var Assembler_x86, instr: string, imm: int, reg: Register) = + # Generate a code fragment + # ⚠️ Warning: + # The caller should deal with destination/source operand + # so that it fits GNU Assembly + if a.wordBitWidth == 64: + a.code &= instr & "q $" & $imm & ", %%" & $reg & '\n' + else: + a.code &= instr & "l $" & $imm & ", %%" & $reg & '\n' + +func codeFragment(a: var Assembler_x86, instr: string, reg0, reg1: Register) = + # Generate a code fragment + # ⚠️ Warning: + # The caller should deal with destination/source operand + # so that it fits GNU Assembly + if a.wordBitWidth == 64: + a.code &= instr & "q %%" & $reg0 & ", %%" & $reg1 & '\n' + else: + a.code &= instr & "l %%" & $reg0 & ", %%" & $reg1 & '\n' + +func codeFragment(a: var Assembler_x86, instr: string, imm: int, reg: OperandReuse) = + # Generate a code fragment + # ⚠️ Warning: + # The caller should deal with destination/source operand + # so that it fits GNU Assembly + if a.wordBitWidth == 64: + a.code &= instr & "q $" & $imm & ", %" & $reg.asmId & '\n' + else: + a.code &= instr & "l $" & $imm & ", %" & $reg.asmId & '\n' + +func codeFragment(a: var Assembler_x86, instr: string, reg0, reg1: OperandReuse) = + # Generate a code fragment + # ⚠️ Warning: + # The caller should deal with destination/source operand + # so that it fits GNU Assembly + if a.wordBitWidth == 64: + a.code &= instr & "q %" & $reg0.asmId & ", %" & $reg1.asmId & '\n' + else: + a.code &= instr & "l %" & $reg0.asmId & ", %" & $reg1.asmId & '\n' + +func codeFragment(a: var Assembler_x86, instr: string, reg0: OperandReuse, reg1: Operand) = + # Generate a code fragment + # ⚠️ Warning: + # The caller should deal with destination/source operand + # so that it fits GNU Assembly + if a.wordBitWidth == 64: + a.code &= instr & "q %" & $reg0.asmId & ", %" & $reg1.desc.asmId & '\n' + else: + a.code &= instr & "l %" & $reg0.asmId & ", %" & $reg1.desc.asmId & '\n' + + a.operands.incl reg1.desc + +func reuseRegister*(reg: OperandArray): OperandReuse = + # TODO: disable the reg input + doAssert reg.buf[0].desc.constraint == InputOutput + result.asmId = reg.buf[0].desc.asmId + +func comment*(a: var Assembler_x86, comment: string) = + # Add a comment + a.code &= "# " & comment & '\n' + +func repackRegisters*(regArr: OperandArray, regs: varargs[Operand]): OperandArray = + ## Extend an array of registers with extra registers + result.buf = regArr.buf + result.buf.add regs + result.nimSymbol = nil + +# Instructions +# ------------------------------------------------------------------------------------------------------------ + +func add*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- dst + src + doAssert dst.desc.constraint in OutputReg + a.codeFragment("add", src, dst) + a.areFlagsClobbered = true + +func adc*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- dst + src + carry + doAssert dst.desc.constraint in OutputReg + a.codeFragment("adc", src, dst) + a.areFlagsClobbered = true + + if dst.desc.rm != Reg: + {.warning: "Using addcarry with a memory destination, this incurs significant performance penalties.".} + +func adc*(a: var Assembler_x86, dst: Operand, imm: int) = + ## Does: dst <- dst + imm + borrow + doAssert dst.desc.constraint in OutputReg + a.codeFragment("adc", imm, dst) + a.areFlagsClobbered = true + + if dst.desc.rm != Reg: + {.warning: "Using addcarry with a memory destination, this incurs significant performance penalties.".} + +func sub*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- dst - src + doAssert dst.desc.constraint in OutputReg + a.codeFragment("sub", src, dst) + a.areFlagsClobbered = true + +func sbb*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- dst - src - borrow + doAssert dst.desc.constraint in OutputReg + a.codeFragment("sbb", src, dst) + a.areFlagsClobbered = true + + if dst.desc.rm != Reg: + {.warning: "Using subborrow with a memory destination, this incurs significant performance penalties.".} + +func sbb*(a: var Assembler_x86, dst: Operand, imm: int) = + ## Does: dst <- dst - imm - borrow + doAssert dst.desc.constraint in OutputReg + a.codeFragment("sbb", imm, dst) + a.areFlagsClobbered = true + + if dst.desc.rm != Reg: + {.warning: "Using subborrow with a memory destination, this incurs significant performance penalties.".} + +func sbb*(a: var Assembler_x86, dst: Register, imm: int) = + ## Does: dst <- dst - imm - borrow + a.codeFragment("sbb", imm, dst) + a.areFlagsClobbered = true + +func sbb*(a: var Assembler_x86, dst, src: Register) = + ## Does: dst <- dst - imm - borrow + a.codeFragment("sbb", src, dst) + a.areFlagsClobbered = true + +func sbb*(a: var Assembler_x86, dst: OperandReuse, imm: int) = + ## Does: dst <- dst - imm - borrow + a.codeFragment("sbb", imm, dst) + a.areFlagsClobbered = true + +func sbb*(a: var Assembler_x86, dst, src: OperandReuse) = + ## Does: dst <- dst - imm - borrow + a.codeFragment("sbb", src, dst) + a.areFlagsClobbered = true + +func sar*(a: var Assembler_x86, dst: Operand, imm: int) = + ## Does Arithmetic Right Shift (i.e. with sign extension) + doAssert dst.desc.constraint in OutputReg + a.codeFragment("sar", imm, dst) + a.areFlagsClobbered = true + +func `and`*(a: var Assembler_x86, dst: OperandReuse, imm: int) = + ## Compute the bitwise AND of x and y and + ## set the Sign, Zero and Parity flags + a.codeFragment("and", imm, dst) + a.areFlagsClobbered = true + +func `and`*(a: var Assembler_x86, dst, src: Operand) = + ## Compute the bitwise AND of x and y and + ## set the Sign, Zero and Parity flags + a.codeFragment("and", src, dst) + a.areFlagsClobbered = true + +func `and`*(a: var Assembler_x86, dst: Operand, src: OperandReuse) = + ## Compute the bitwise AND of x and y and + ## set the Sign, Zero and Parity flags + a.codeFragment("and", src, dst) + a.areFlagsClobbered = true + +func test*(a: var Assembler_x86, x, y: Operand) = + ## Compute the bitwise AND of x and y and + ## set the Sign, Zero and Parity flags + a.codeFragment("test", x, y) + a.areFlagsClobbered = true + +func `xor`*(a: var Assembler_x86, x, y: Operand) = + ## Compute the bitwise xor of x and y and + ## reset all flags + a.codeFragment("xor", x, y) + a.areFlagsClobbered = true + +func mov*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- src + doAssert dst.desc.constraint in OutputReg, $dst.repr + + a.codeFragment("mov", src, dst) + # No clobber + +func mov*(a: var Assembler_x86, dst: Operand, imm: int) = + ## Does: dst <- imm + doAssert dst.desc.constraint in OutputReg, $dst.repr + + a.codeFragment("mov", imm, dst) + # No clobber + +func cmovc*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- src if the carry flag is set + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + doAssert dst.desc.constraint in OutputReg, $dst.repr + + a.codeFragment("cmovc", src, dst) + # No clobber + +func cmovnc*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- src if the carry flag is not set + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + doAssert dst.desc.constraint in {Output_EarlyClobber, InputOutput, Output_Overwrite}, $dst.repr + + a.codeFragment("cmovnc", src, dst) + # No clobber + +func cmovz*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- src if the zero flag is not set + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + doAssert dst.desc.constraint in OutputReg, $dst.repr + + a.codeFragment("cmovz", src, dst) + # No clobber + +func cmovnz*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- src if the zero flag is not set + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + doAssert dst.desc.constraint in OutputReg, $dst.repr + + a.codeFragment("cmovnz", src, dst) + # No clobber + +func cmovs*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- src if the sign flag + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + doAssert dst.desc.constraint in OutputReg, $dst.repr + + a.codeFragment("cmovs", src, dst) + # No clobber + +func mul*(a: var Assembler_x86, dHi, dLo: Register, src0: Operand, src1: Register) = + ## Does (dHi, dLo) <- src0 * src1 + doAssert src1 == rax, "MUL requires the RAX register" + doAssert dHi == rdx, "MUL requires the RDX register" + doAssert dLo == rax, "MUL requires the RAX register" + + a.codeFragment("mul", src0) + +func imul*(a: var Assembler_x86, dst, src: Operand) = + ## Does dst <- dst * src, keeping only the low half + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + doAssert dst.desc.constraint in OutputReg, $dst.repr + + a.codeFragment("imul", src, dst) + +func mulx*(a: var Assembler_x86, dHi, dLo, src0: Operand, src1: Register) = + ## Does (dHi, dLo) <- src0 * src1 + doAssert src1 == rdx, "MULX requires the RDX register" + doAssert dHi.desc.rm in {Reg, ElemsInReg} or dHi.desc.rm in SpecificRegisters, + "The destination operand must be a register " & $dHi.repr + doAssert dLo.desc.rm in {Reg, ElemsInReg} or dLo.desc.rm in SpecificRegisters, + "The destination operand must be a register " & $dLo.repr + doAssert dHi.desc.constraint in OutputReg + doAssert dLo.desc.constraint in OutputReg + + let off0 = a.getStrOffset(src0) + + # Annoying AT&T syntax + if a.wordBitWidth == 64: + a.code &= "mulxq " & off0 & ", %" & $dLo.desc.asmId & ", %" & $dHi.desc.asmId & '\n' + else: + a.code &= "mulxl " & off0 & ", %" & $dLo.desc.asmId & ", %" & $dHi.desc.asmId & '\n' + + a.operands.incl src0.desc + +func adcx*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- dst + src + carry + ## and only sets the carry flag + doAssert dst.desc.constraint in OutputReg, $dst.repr + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + a.codeFragment("adcx", src, dst) + a.areFlagsClobbered = true + +func adox*(a: var Assembler_x86, dst, src: Operand) = + ## Does: dst <- dst + src + overflow + ## and only sets the overflow flag + doAssert dst.desc.constraint in OutputReg, $dst.repr + doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr + a.codeFragment("adox", src, dst) + a.areFlagsClobbered = true + +func push*(a: var Assembler_x86, _: type Stack, reg: Operand) = + ## Push the content of register on the stack + doAssert reg.desc.rm in {Reg, PointerInReg, ElemsInReg}+SpecificRegisters, "The destination operand must be a register: " & $reg.repr + a.codeFragment("push", reg) + a.isStackClobbered = true + +func pop*(a: var Assembler_x86, _: type Stack, reg: Operand) = + ## Pop the content of register on the stack + doAssert reg.desc.rm in {Reg, PointerInReg, ElemsInReg}+SpecificRegisters, "The destination operand must be a register: " & $reg.repr + a.codeFragment("pop", reg) + a.isStackClobbered = true diff --git a/constantine/primitives/research/README.md b/constantine/primitives/research/README.md deleted file mode 100644 index 32cc175..0000000 --- a/constantine/primitives/research/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Compiler for generic inline assembly code-generation - -This folder holds alternative implementations of primitives -that uses inline assembly. - -This avoids the pitfalls of traditional compiler bad code generation -for multiprecision arithmetic (see GCC https://gcc.godbolt.org/z/2h768y) -or unsupported features like handling 2 carry chains for -multiplication using MULX/ADOX/ADCX. - -To be generic over multiple curves, -for example BN254 requires 4 words and BLS12-381 requires 6 words of size 64 bits, -the compilers is implemented as a set of macros that generate inline assembly. - -⚠⚠⚠ Warning! Warning! Warning! - -This is a significant sacrifice of code readability, portability, auditability and maintainability in favor of performance. - -This combines 2 of the most notorious ways to obfuscate your code: -* metaprogramming and macros -* inline assembly - -Adventurers beware: not for the faint of heart. - -This is unfinished, untested, unused, unfuzzed and just a proof-of-concept at the moment.* - -_* I take no responsibility if this smashes your stack, eats your cat, hides a skeleton in your closet, warps a pink elephant in the room, summons untold eldritch horrors or causes the heat death of the universe. You have been warned._ - -_The road to debugging hell is paved with metaprogrammed assembly optimizations._ - -_For my defence, OpenSSL assembly is generated by a Perl script and neither Perl nor the generated Assembly are type-checked by a dependently-typed compiler._ - -## References - -Multiprecision (Montgomery) Multiplication & Squaring in Assembly - -- Intel MULX/ADCX/ADOX Table 2 p13: https://www.intel.cn/content/dam/www/public/us/en/documents/white-papers/ia-large-integer-arithmetic-paper.pdf -- Squaring: https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/large-integer-squaring-ia-paper.pdf -- https://eprint.iacr.org/eprint-bin/getfile.pl?entry=2017/558&version=20170608:200345&file=558.pdf -- https://github.com/intel/ipp-crypto -- https://github.com/herumi/mcl - -Experimentations in Nim - -- https://github.com/mratsim/finite-fields diff --git a/constantine/primitives/research/addcarry_subborrow_compiler.nim b/constantine/primitives/research/addcarry_subborrow_compiler.nim deleted file mode 100644 index 3391577..0000000 --- a/constantine/primitives/research/addcarry_subborrow_compiler.nim +++ /dev/null @@ -1,133 +0,0 @@ -# Constantine -# Copyright (c) 2018-2019 Status Research & Development GmbH -# Copyright (c) 2020-Present Mamy André-Ratsimbazafy -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -# ############################################################ -# -# Add-with-carry and Sub-with-borrow -# -# ############################################################ -# -# This is a proof-of-concept optimal add-with-carry -# compiler implemented as Nim macros. -# -# This overcome the bad GCC codegen aven with addcary_u64 intrinsic. - -import std/macros - -func wordsRequired(bits: int): int {.compileTime.} = - ## Compute the number of limbs required - ## from the announced bit length - (bits + 64 - 1) div 64 - -type - BigInt[bits: static int] {.byref.} = object - ## BigInt - ## Enforce-passing by reference otherwise uint128 are passed by stack - ## which causes issue with the inline assembly - limbs: array[bits.wordsRequired, uint64] - -macro addCarryGen_u64(a, b: untyped, bits: static int): untyped = - var asmStmt = (block: - " movq %[b], %[tmp]\n" & - " addq %[tmp], %[a]\n" - ) - - let maxByteOffset = bits div 8 - const wsize = sizeof(uint64) - - when defined(gcc): - for byteOffset in countup(wsize, maxByteOffset-1, wsize): - asmStmt.add (block: - "\n" & - # movq 8+%[b], %[tmp] - " movq " & $byteOffset & "+%[b], %[tmp]\n" & - # adcq %[tmp], 8+%[a] - " adcq %[tmp], " & $byteOffset & "+%[a]\n" - ) - elif defined(clang): - # https://lists.llvm.org/pipermail/llvm-dev/2017-August/116202.html - for byteOffset in countup(wsize, maxByteOffset-1, wsize): - asmStmt.add (block: - "\n" & - # movq 8+%[b], %[tmp] - " movq " & $byteOffset & "%[b], %[tmp]\n" & - # adcq %[tmp], 8+%[a] - " adcq %[tmp], " & $byteOffset & "%[a]\n" - ) - - let tmp = ident("tmp") - asmStmt.add (block: - ": [tmp] \"+r\" (`" & $tmp & "`), [a] \"+m\" (`" & $a & "->limbs[0]`)\n" & - ": [b] \"m\"(`" & $b & "->limbs[0]`)\n" & - ": \"cc\"" - ) - - result = newStmtList() - result.add quote do: - var `tmp`{.noinit.}: uint64 - - result.add nnkAsmStmt.newTree( - newEmptyNode(), - newLit asmStmt - ) - - echo result.toStrLit - -func `+=`(a: var BigInt, b: BigInt) {.noinline.}= - # Depending on inline or noinline - # the generated ASM addressing must be tweaked for Clang - # https://lists.llvm.org/pipermail/llvm-dev/2017-August/116202.html - addCarryGen_u64(a, b, BigInt.bits) - -# ############################################# -when isMainModule: - import std/random - proc rand(T: typedesc[BigInt]): T = - for i in 0 ..< result.limbs.len: - result.limbs[i] = uint64(rand(high(int))) - - proc main() = - block: - let a = BigInt[128](limbs: [high(uint64), 0]) - let b = BigInt[128](limbs: [1'u64, 0]) - - echo "a: ", a - echo "b: ", b - echo "------------------------------------------------------" - - var a1 = a - a1 += b - echo a1 - echo "======================================================" - - block: - let a = rand(BigInt[256]) - let b = rand(BigInt[256]) - - echo "a: ", a - echo "b: ", b - echo "------------------------------------------------------" - - var a1 = a - a1 += b - echo a1 - echo "======================================================" - - block: - let a = rand(BigInt[384]) - let b = rand(BigInt[384]) - - echo "a: ", a - echo "b: ", b - echo "------------------------------------------------------" - - var a1 = a - a1 += b - echo a1 - - main() diff --git a/tests/t_ec_sage_bls12_381 b/tests/t_ec_sage_bls12_381 deleted file mode 100755 index a40778a09a448d183e602e8eaa7fba9ffdbe7605..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 491008 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 Kn*H_1_WvJu!q_hW diff --git a/tests/t_finite_fields_vs_gmp.nim b/tests/t_finite_fields_vs_gmp.nim index 20d0749..af96673 100644 --- a/tests/t_finite_fields_vs_gmp.nim +++ b/tests/t_finite_fields_vs_gmp.nim @@ -20,22 +20,13 @@ import echo "\n------------------------------------------------------\n" var RNG {.compileTime.} = initRand(1234) -const CurveParams = [ - P224, - BN254_Nogami, - BN254_Snarks, - Curve25519, - P256, - Secp256k1, - BLS12_377, - BLS12_381, - BN446, - FKM12_447, - BLS12_461, - BN462 -] -const AvailableCurves = [P224, BN254_Nogami, BN254_Snarks, P256, Secp256k1, BLS12_381] +const AvailableCurves = [ + P224, + BN254_Nogami, BN254_Snarks, + P256, Secp256k1, + BLS12_381 +] const # https://gmplib.org/manual/Integer-Import-and-Export.html GMP_WordLittleEndian = -1'i32 diff --git a/tests/t_io_fields.nim b/tests/t_io_fields.nim index bfdaa68..520eb2f 100644 --- a/tests/t_io_fields.nim +++ b/tests/t_io_fields.nim @@ -140,6 +140,14 @@ proc main() = check: p == hex + test "Round trip on prime field of BN254 Snarks curve": + block: # 2^126 + const p = "0x0000000000000000000000000000000040000000000000000000000000000000" + let x = Fp[BN254_Snarks].fromBig BigInt[254].fromHex(p) + let hex = x.toHex(bigEndian) + + check: p == hex + test "Round trip on prime field of BLS12_381 curve": block: # 2^126 const p = "0x000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000"