add native nim mode (#142)

* add native nim mode

* don't try to link wasm-c code
This commit is contained in:
Jacek Sieka 2023-09-25 16:49:04 +02:00 committed by GitHub
parent 54e24cae41
commit 711cda4456
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 73 deletions

1
nim.cfg Normal file
View File

@ -0,0 +1 @@
nimcache = "build/nimcache/$projectName"

View File

@ -10,24 +10,42 @@ skipDirs = @["tests", "benchmarks"]
requires "nim >= 1.6.12",
"stew"
proc test(args, path: string) =
if not dirExists "build":
mkDir "build"
let nimc = getEnv("NIMC", "nim") # Which nim compiler to use
let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js)
let flags = getEnv("NIMFLAGS", "") # Extra flags for the compiler
let verbose = getEnv("V", "") notin ["", "0"]
exec "nim " & getEnv("TEST_LANG", "c") & " " & getEnv("NIMFLAGS") & " " & args &
" --outdir:build -r --hints:off --warnings:off --skipParentCfg" &
" --styleCheck:usages --styleCheck:error " & path
from os import quoteShell
let cfg =
" --styleCheck:usages --styleCheck:error" &
(if verbose: "" else: " --verbosity:0 --hints:off") &
" --skipParentCfg --skipUserCfg --outdir:build " &
quoteShell("--nimcache:build/nimcache/$projectName")
proc build(args, path: string) =
exec nimc & " " & lang & " " & cfg & " " & flags & " " & args & " " & path
proc run(args, path: string) =
build args & " -r", path
if (NimMajor, NimMinor) > (1, 6):
exec "nim " & getEnv("TEST_LANG", "c") & " " & getEnv("NIMFLAGS") & " " & args &
" --outdir:build -r --mm:refc --hints:off --warnings:off --skipParentCfg" &
" --styleCheck:usages --styleCheck:error " & path
build args & " --mm:refc -r", path
proc test(path: string) =
for config in ["", "-d:stintNoIntrinsics"]:
for mode in ["-d:debug", "-d:release"]:
run(config & " " & mode, path)
task test_internal, "Run tests for internal procs":
test "", "tests/internal"
test "tests/internal"
task test_public_api, "Run all tests - prod implementation (StUint[64] = uint64":
test "", "tests/all_tests"
test "tests/all_tests"
task test, "Run all tests":
exec "nimble test_internal"
exec "nimble test_public_api"
test "tests/internal"
test "tests/all_tests"
# Smoke-test wasm32 compiles
build "--cpu:wasm32 -c", "tests/all_tests"

14
stint/config.nim Normal file
View File

@ -0,0 +1,14 @@
# Stint
# Copyright 2023 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
const
stintNoIntrinsics* {.booldefine.} = false
## Use only native Nim code without intrinsics, emit or asm - useful for
## targets such as wasm and compilers with no native int128 support (and the
## vm!)

View File

@ -53,6 +53,7 @@ const X86* = defined(amd64) or defined(i386)
when sizeof(int) == 8 and GCC_Compatible:
type
uint128*{.importc: "unsigned __int128".} = object
hi, lo: uint64
# Accessors
# --------------------------------------------------------

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../datatypes, ./compiletime_fallback
import ../../config, ../datatypes, ./compiletime_fallback
# ############################################################
#
@ -84,7 +84,11 @@ import ../datatypes, ./compiletime_fallback
# Note: GCC before 2017 had incorrect codegen in some cases:
# - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81300
when X86:
const
useIntrinsics = X86 and not stintNoIntrinsics
useInt128 = GCC_Compatible and sizeof(int) == 8 and not stintNoIntrinsics
when useIntrinsics:
when defined(windows):
{.pragma: intrinsics, header:"<intrin.h>", nodecl.}
else:
@ -105,37 +109,35 @@ when X86:
func addC*(cOut: var Carry, sum: var uint32, a, b: uint32, cIn: Carry) {.inline.} =
## Addition with carry
## (CarryOut, Sum) <- a + b + CarryIn
when nimvm:
template native =
let dblPrec = uint64(cIn) + uint64(a) + uint64(b)
sum = uint32(dblPrec and uint32.high)
cOut = Carry(dblPrec shr 32)
when nimvm:
native
else:
when X86:
when useIntrinsics:
cOut = addcarry_u32(cIn, a, b, sum)
else:
# on arch e.g. arm: nim will complaints "Error: redefinition of 'dblPrec'"
# so we use dlbPrec2 here
let dblPrec2 = uint64(cIn) + uint64(a) + uint64(b)
sum = uint32(dblPrec2)
cOut = Carry(dblPrec2 shr 32)
native
func subB*(bOut: var Borrow, diff: var uint32, a, b: uint32, bIn: Borrow) {.inline.} =
## Substraction with borrow
## (BorrowOut, Diff) <- a - b - borrowIn
when nimvm:
template native =
let dblPrec = uint64(a) - uint64(b) - uint64(bIn)
diff = uint32(dblPrec and uint32.high)
# On borrow the high word will be 0b1111...1111 and needs to be masked
bOut = Borrow((dblPrec shr 32) and 1)
when nimvm:
native
else:
when X86:
when useIntrinsics:
bOut = subborrow_u32(bIn, a, b, diff)
else:
# ditto
let dblPrec2 = uint64(a) - uint64(b) - uint64(bIn)
diff = uint32(dblPrec2)
# On borrow the high word will be 0b1111...1111 and needs to be masked
bOut = Borrow((dblPrec2 shr 32) and 1)
native
func addC*(cOut: var Carry, sum: var uint64, a, b: uint64, cIn: Carry) {.inline.} =
## Addition with carry
@ -143,24 +145,21 @@ func addC*(cOut: var Carry, sum: var uint64, a, b: uint64, cIn: Carry) {.inline.
when nimvm:
addC_nim(cOut, sum, a, b, cIn)
else:
when X86:
when useIntrinsics:
cOut = addcarry_u64(cIn, a, b, sum)
elif useInt128:
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," + (unsigned __int128)", b, " + (unsigned __int128)",cIn,";"].}
# Don't forget to dereference the var param in C mode
when defined(cpp):
{.emit:[cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:[sum, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:["*",sum, " = (NU64)", dblPrec,";"].}
else:
block:
static:
doAssert GCC_Compatible
doAssert sizeof(int) == 8
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," + (unsigned __int128)", b, " + (unsigned __int128)",cIn,";"].}
# Don't forget to dereference the var param in C mode
when defined(cpp):
{.emit:[cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:[sum, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:["*",sum, " = (NU64)", dblPrec,";"].}
addC_nim(cOut, sum, a, b, cIn)
func subB*(bOut: var Borrow, diff: var uint64, a, b: uint64, bIn: Borrow) {.inline.} =
## Substraction with borrow
@ -168,22 +167,19 @@ func subB*(bOut: var Borrow, diff: var uint64, a, b: uint64, bIn: Borrow) {.inli
when nimvm:
subB_nim(bOut, diff, a, b, bIn)
else:
when X86:
when useIntrinsics:
bOut = subborrow_u64(bIn, a, b, diff)
elif useInt128:
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," - (unsigned __int128)", b, " - (unsigned __int128)",bIn,";"].}
# Don't forget to dereference the var param in C mode
# On borrow the high word will be 0b1111...1111 and needs to be masked
when defined(cpp):
{.emit:[bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:[diff, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:["*",diff, " = (NU64)", dblPrec,";"].}
else:
block:
static:
doAssert GCC_Compatible
doAssert sizeof(int) == 8
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," - (unsigned __int128)", b, " - (unsigned __int128)",bIn,";"].}
# Don't forget to dereference the var param in C mode
# On borrow the high word will be 0b1111...1111 and needs to be masked
when defined(cpp):
{.emit:[bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:[diff, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:["*",diff, " = (NU64)", dblPrec,";"].}
subB_nim(bOut, diff, a, b, bIn)

View File

@ -82,16 +82,19 @@ func muladd2*(hi, lo: var uint32, a, b, c1, c2: uint32) {.inline.}=
# ############################################################
when sizeof(int) == 8 and not defined(Stint32):
import ../../config
from ./compiletime_fallback import div2n1n_nim, mul_nim, muladd1_nim, muladd2_nim
when defined(vcc):
from ./extended_precision_x86_64_msvc import div2n1n_128, mul_128, muladd1_128, muladd2_128
elif GCC_Compatible:
when X86:
from ./extended_precision_x86_64_gcc import div2n1n_128
from ./extended_precision_64bit_uint128 import mul_128, muladd1_128, muladd2_128
when not stintNoIntrinsics:
when defined(vcc):
from ./extended_precision_x86_64_msvc import div2n1n_128, mul_128, muladd1_128, muladd2_128
else:
from ./extended_precision_64bit_uint128 import div2n1n_128, mul_128, muladd1_128, muladd2_128
when defined(amd64):
from ./extended_precision_x86_64_gcc import div2n1n_128
from ./extended_precision_64bit_uint128 import mul_128, muladd1_128, muladd2_128
else:
from ./extended_precision_64bit_uint128 import div2n1n_128, mul_128, muladd1_128, muladd2_128
func mul*(hi, lo: var uint64, u, v: uint64) {.inline.}=
## Extended precision multiplication
@ -99,7 +102,10 @@ when sizeof(int) == 8 and not defined(Stint32):
when nimvm:
mul_nim(hi, lo, u, v)
else:
mul_128(hi, lo, u, v)
when stintNoIntrinsics:
mul_nim(hi, lo, u, v)
else:
mul_128(hi, lo, u, v)
func muladd1*(hi, lo: var uint64, a, b, c: uint64) {.inline.}=
## Extended precision multiplication + addition
@ -110,7 +116,10 @@ when sizeof(int) == 8 and not defined(Stint32):
when nimvm:
muladd1_nim(hi, lo, a, b, c)
else:
muladd1_128(hi, lo, a, b, c)
when stintNoIntrinsics:
muladd1_nim(hi, lo, a, b, c)
else:
muladd1_128(hi, lo, a, b, c)
func muladd2*(hi, lo: var uint64, a, b, c1, c2: uint64) {.inline.}=
## Extended precision multiplication + addition + addition
@ -122,7 +131,10 @@ when sizeof(int) == 8 and not defined(Stint32):
when nimvm:
muladd2_nim(hi, lo, a, b, c1, c2)
else:
muladd2_128(hi, lo, a, b, c1, c2)
when stintNoIntrinsics:
muladd2_nim(hi, lo, a, b, c1, c2)
else:
muladd2_128(hi, lo, a, b, c1, c2)
func div2n1n*(q, r: var uint64, n_hi, n_lo, d: uint64) {.inline.}=
## Division uint128 by uint64
@ -132,7 +144,10 @@ when sizeof(int) == 8 and not defined(Stint32):
when nimvm:
div2n1n_nim(q, r, n_hi, n_lo, d)
else:
div2n1n_128(q, r, n_hi, n_lo, d)
when stintNoIntrinsics:
div2n1n_nim(q, r, n_hi, n_lo, d)
else:
div2n1n_128(q, r, n_hi, n_lo, d)
# ############################################################
#

View File

@ -9,6 +9,8 @@
# Test implementation of internal proc:
{.used.}
include ../stint/private/uint_div
import unittest