Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wrap dragonbox from https://github.com/jk-jeon/dragonbox #732

Draft
wants to merge 32 commits into
base: devel
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3824fd3
RST opt.list to have priority over def.list (#17845)
a-mr May 15, 2021
b58d48c
add https://github.com/abolz/Drachennest/blob/master/src/dragonbox.cc
timotheecour May 12, 2021
893f0a7
faster addFloat using dragonbox algorithm
timotheecour May 12, 2021
9971e88
works with addDependency etc
timotheecour May 13, 2021
9ed49f6
fixup
timotheecour May 13, 2021
6bc463e
add benchmark
timotheecour May 13, 2021
28164bc
improve tests
timotheecour May 13, 2021
5f3d892
fixup
timotheecour May 13, 2021
bdd4e9f
fix tests
timotheecour May 14, 2021
073094f
add test
timotheecour May 14, 2021
80128aa
fixup
timotheecour May 14, 2021
5c02172
fixup
timotheecour May 14, 2021
d027a0d
fix treadlines
timotheecour May 14, 2021
b661f3f
fix some tests
timotheecour May 14, 2021
3806a20
fix test
timotheecour May 14, 2021
0f1d00c
fix test
timotheecour May 14, 2021
3e53aad
fix test
timotheecour May 14, 2021
9e68fdd
PRTEMP
timotheecour May 14, 2021
886f845
fix for ic
timotheecour May 14, 2021
5494f04
cleanups
timotheecour May 14, 2021
97d2abc
fixup
timotheecour May 14, 2021
f265983
add back (but deprecate) system/formatfloat as a shallow wrapper
timotheecour May 14, 2021
746258b
move to lib/vendor/dragonbox.cc
timotheecour May 14, 2021
364c292
reorg + add LICENSE
timotheecour May 14, 2021
a173ca9
improve tdependency_utils.nim
timotheecour May 14, 2021
bdd989f
dependency_utils => deputils
timotheecour May 14, 2021
239e2c6
fixup
timotheecour May 14, 2021
c565d38
improve tstrfloats_bench
timotheecour May 14, 2021
df4ec8f
fixup
timotheecour May 15, 2021
34a6ea0
wrap dragonbox from https://github.com/jk-jeon/dragonbox
timotheecour May 15, 2021
2ea8423
PRTEMP
timotheecour May 15, 2021
097fc18
add schubfach_64, schubfach_32
timotheecour May 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
- `jsonutils` now serializes/deserializes holey enums as regular enums (via `ord`) instead of as strings.
Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period.

- `system.addFloat` now uses dragonbox algorithm, which ensures roundtrip guarantee, minimum length,
and correct rounding. Use `-d:nimLegacyAddFloat` for a transition period.

- `system/formatfloat` (an internal module) was deprecated; use the new module `std/strfloats` instead.

## Standard library additions and changes
- Added support for parenthesized expressions in `strformat`
Expand Down
2 changes: 1 addition & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ type
mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples,
mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
mSymIsInstantiationOf, mNodeId, mPrivateAccess
mSymIsInstantiationOf, mNodeId, mPrivateAccess, mAddDependency


# things that we can evaluate safely at compile time, even if not asked for it:
Expand Down
69 changes: 69 additions & 0 deletions compiler/builddeps.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#[
## TODO
* this will show `CC: dragonbox`:
`nim r -f --hint:cc --filenames:abs --processing:filenames --hint:conf nonexistant`
we could refine the logic to delay compilation until cgen phase instead.
(see also `tests/osproc/treadlines.nim`)
* allow some form of reporting so that caller can tell whether a dependency
doesn't exist, or was already built, or builds with error, or builds successfully, etc.
* allow some way to expose this to user code
]#

import std/[osproc, os, strutils]
import msgs, options, ast, lineinfos, extccomp, pathutils

const prefix = "__" # prevent clashing with user files

type Job = object
input: string
copts: string

proc addDependencyImpl(conf: ConfigRef, name: string, info: TLineInfo, jobs: seq[Job], linkerFlags = "") =
if name in conf.dependencies: return
conf.dependencies.add name
# xxx we could also build this under $nimb/build/
let dir = conf.getNimcacheDir().string
createDir dir
for job in jobs:
let objFile = dir / ("$1_$2_$3.o" % [prefix, name, job.input.splitFile.name])
if optForceFullMake in conf.globalOptions or not objFile.fileExists:
# xxx use `getCompilerExe`
when defined(osx):
let cppExe = "clang++"
else:
let cppExe = "g++"
when defined(linux):
# xxx: avoids linker errors:
# relocation R_X86_64_32S against `.rodata' can not be used when making a PIE object; recompile with -fPIE
let options = "-fPIE"
else:
let options = ""
let inputFile = conf.libpath.string / job.input
let cmd = "$# -c $# $# -O3 -o $# $#" % [cppExe.quoteShell, job.copts, options, objFile.quoteShell, inputFile.quoteShell]
# xxx use md5 hash to recompile if needed
writePrettyCmdsStderr displayProgressCC(conf, inputFile, cmd)
let (outp, status) = execCmdEx(cmd)
if status != 0:
localError(conf, info, "building '$#' failed: cmd: $#\noutput:\n$#" % [name, cmd, outp])
conf.addExternalFileToLink(objFile.AbsoluteFile)
if linkerFlags.len > 0:
conf.addLinkOption(linkerFlags)

proc addDependency*(conf: ConfigRef, name: string, info: TLineInfo) =
case name
of "drachennest_dragonbox":
let copts = "-std=c++11"
var jobs: seq[Job]
for file in "impl.cc dragonbox.cc schubfach_32.cc schubfach_64.cc".split:
jobs.add Job(input: "vendor/drachennest" / file, copts: copts)
addDependencyImpl(conf, name, info, jobs = jobs)
of "dragonbox_dragonbox":
let dir = "vendor/dragonbox"
let includeDir = conf.libpath.string / dir / "include"
let copts = "-std=c++17 -I $#" % includeDir.quoteShell
var jobs: seq[Job]
for file in "impl.cc dragonbox_to_chars.cpp".split:
jobs.add Job(input: dir / file, copts: copts)
addDependencyImpl(conf, name, info, jobs = jobs, linkerFlags = "-lc++")
else:
localError(conf, info, "expected: 'dragonbox', got: '$1'" % name)
1 change: 1 addition & 0 deletions compiler/condsyms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasUnifiedTuple")
defineSymbol("nimHasIterable")
defineSymbol("nimHasTypeofVoid")
defineSymbol("nimHasDragonbox")
4 changes: 2 additions & 2 deletions compiler/extccomp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ const

hExt* = ".h"

template writePrettyCmdsStderr(cmd) =
template writePrettyCmdsStderr*(cmd) =
if cmd.len > 0:
flushDot(conf)
stderr.writeLine(cmd)
Expand Down Expand Up @@ -835,7 +835,7 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu
else: platform.OS[conf.target.targetOS].dllFrmt % basename
result = conf.getNimcacheDir / RelativeFile(targetName)

proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
proc displayProgressCC*(conf: ConfigRef, path, compileCmd: string): string =
if conf.hasHint(hintCC):
if optListCmd in conf.globalOptions or conf.verbosity > 1:
result = MsgKindToStr[hintCC] % (demanglePackageName(path.splitFile.name) & ": " & compileCmd)
Expand Down
3 changes: 2 additions & 1 deletion compiler/ic/replayer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
## support.

import ".." / [ast, modulegraphs, trees, extccomp, btrees,
msgs, lineinfos, pathutils, options, cgmeth]
msgs, lineinfos, pathutils, options, cgmeth, builddeps]

import tables

Expand All @@ -32,6 +32,7 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph) =
of "hint": message(g.config, n.info, hintUser, n[1].strVal)
of "warning": message(g.config, n.info, warnUser, n[1].strVal)
of "error": localError(g.config, n.info, errUser, n[1].strVal)
of "addDependency": addDependency(g.config, n[1].strVal, n.info)
of "compile":
internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit
let cname = AbsoluteFile n[1].strVal
Expand Down
3 changes: 2 additions & 1 deletion compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ type

externalToLink*: seq[string] # files to link in addition to the file
# we compiled (*)
dependencies*: seq[string] # dependencies
linkOptionsCmd*: string
compileOptionsCmd*: seq[string]
linkOptions*: string # (*)
Expand Down Expand Up @@ -398,7 +399,7 @@ proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) =
proc hasHint*(conf: ConfigRef, note: TNoteKind): bool =
# ternary states instead of binary states would simplify logic
if optHints notin conf.options: false
elif note in {hintConf, hintProcessing}:
elif note in {hintConf, hintProcessing, hintCC}:
# could add here other special notes like hintSource
# these notes apply globally.
note in conf.mainPackageNotes
Expand Down
6 changes: 0 additions & 6 deletions compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
isStatement: bool = false)

proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
var recorded = newNodeI(nkReplayAction, n.info)
for i in 0..args.high:
recorded.add newStrNode(args[i], n.info)
addPragmaComputation(c, recorded)

const
errStringLiteralExpected = "string literal expected"
errIntLiteralExpected = "integer literal expected"
Expand Down
7 changes: 7 additions & 0 deletions compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ proc addPragmaComputation*(c: PContext; n: PNode) =
if c.config.symbolFiles != disabledSf:
addPragmaComputation(c.encoder, c.packedRepr, n)

proc recordPragma*(c: PContext; n: PNode; args: varargs[string]) =
# xxx rename to `recordAction`, since it can record things other than pragmas
var recorded = newNodeI(nkReplayAction, n.info)
for i in 0..args.high:
recorded.add newStrNode(args[i], n.info)
addPragmaComputation(c, recorded)

proc inclSym(sq: var seq[PSym], s: PSym): bool =
for i in 0..<sq.len:
if sq[i].id == s.id: return false
Expand Down
9 changes: 8 additions & 1 deletion compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# This include file implements the semantic checking for magics.
# included from sem.nim

from builddeps import addDependency
proc semAddrArg(c: PContext; n: PNode; isUnsafeAddr = false): PNode =
let x = semExprWithType(c, n)
if x.kind == nkSym:
Expand Down Expand Up @@ -573,6 +573,13 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}:
n[0].sym.magic = mSubU
result = n
of mAddDependency:
let x = c.semConstExpr(c, n[1])
case x.kind
of nkStrKinds: addDependency(c.config, x.strVal, n.info)
else: localError(c.config, n.info, "cannot evaluate at compile time")
recordPragma(c, n, "addDependency", x.strVal)
result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid))
of mPrivateAccess:
var t = n[1].typ[0]
if t.kind in {tyRef, tyPtr}: t = t[0]
Expand Down
32 changes: 17 additions & 15 deletions compiler/vmhooks.nim
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,32 @@ proc setResult*(a: VmArgs; v: seq[string]) =
for x in v: n.add newStrNode(nkStrLit, x)
a.slots[a.ra].node = n

template getX(k, field) {.dirty.} =
template getReg(a, i): untyped =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == k
result = a.slots[i+a.rb+1].field
a.slots[i+a.rb+1].unsafeAddr

template getX(k, field): untyped {.dirty.} =
let p = getReg(a, i)
doAssert p.kind == k, $p.kind
p.field

proc numArgs*(a: VmArgs): int =
result = a.rc-1

proc getInt*(a: VmArgs; i: Natural): BiggestInt = getX(rkInt, intVal)
proc getBool*(a: VmArgs; i: Natural): bool = getInt(a, i) != 0
proc getFloat*(a: VmArgs; i: Natural): BiggestFloat = getX(rkFloat, floatVal)
proc getString*(a: VmArgs; i: Natural): string =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == rkNode
result = a.slots[i+a.rb+1].node.strVal

proc getNode*(a: VmArgs; i: Natural): PNode =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == rkNode
result = a.slots[i+a.rb+1].node
proc getNode*(a: VmArgs; i: Natural): PNode = getX(rkNode, node)
proc getString*(a: VmArgs; i: Natural): string = getX(rkNode, node).strVal
proc getVar*(a: VmArgs; i: Natural): PNode =
let p = getReg(a, i)
# depending on whether we come from top-level or proc scope, we need to consider 2 cases
case p.kind
of rkRegisterAddr: result = p.regAddr.node
of rkNodeAddr: result = p.nodeAddr[]
else: doAssert false, $p.kind

proc getNodeAddr*(a: VmArgs; i: Natural): PNode =
doAssert i < a.rc-1
doAssert a.slots[i+a.rb+1].kind == rkNodeAddr
let nodeAddr = a.slots[i+a.rb+1].nodeAddr
let nodeAddr = getX(rkNodeAddr, nodeAddr)
doAssert nodeAddr != nil
result = nodeAddr[]
14 changes: 10 additions & 4 deletions compiler/vmops.nim
Original file line number Diff line number Diff line change
Expand Up @@ -234,18 +234,19 @@ proc registerAdditionalOps*(c: PCtx) =
registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
setResult(a, getAppFilename())

proc stackTrace2(msg: string, n: PNode) =
stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info)

registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) =
let n = getNode(a, 0)
if n.kind != nkSym:
stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr,
"symBodyHash() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info)
stackTrace2("symBodyHash() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n)
setResult(a, $symBodyDigest(c.graph, n.sym))

registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) =
let n = getNode(a, 0)
if n.kind != nkSym:
stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr,
"isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info)
stackTrace2("isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n)
setResult(a, sfExported in n.sym.flags)

proc hashVmImpl(a: VmArgs) =
Expand Down Expand Up @@ -320,3 +321,8 @@ proc registerAdditionalOps*(c: PCtx) =
registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) =
let fn = getNode(a, 0)
setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure))

registerCallback c, "stdlib.strfloats.addFloat", proc(a: VmArgs) =
let p = a.getVar(0)
let x = a.getFloat(1)
addFloat(p.strVal, x)
4 changes: 2 additions & 2 deletions lib/packages/docutils/rst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1867,10 +1867,10 @@ proc whichSection(p: RstParser): RstNodeKind =
elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or
match(p, p.idx, "e. "):
result = rnEnumList
elif isDefList(p):
result = rnDefList
elif isOptionList(p):
result = rnOptionList
elif isDefList(p):
result = rnDefList
else:
result = rnParagraph
of tkWord, tkOther, tkWhite:
Expand Down
8 changes: 8 additions & 0 deletions lib/std/private/deputils.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
##[
Experimental, subject to change
]##

proc addDependency*(name: string) {.magic: "AddDependency".} =
## Adds a dependency on `name`; currently only `dragonbox` is supported.
# a pragma would be possible but cause more boilerplate, and also would be less flexible
# in case we want to also return something about the depenedncy
Loading