# # # The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This module declares some helpers for the C code generator. import ast, types, msgs, wordrecg, platform, trees, options, cgendata, mangleutils, renderer, modulegraphs, bnode import std/[hashes, strutils, formatfloat] when defined(nimPreviewSlimSystem): import std/assertions proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = case n.kind of nkStmtList: result = nil for it in sons(n): result = getPragmaStmt(it, w) if result != nil: break of nkPragma: result = nil for it in sons(n): if whichPragma(it) == w: return it else: result = nil proc stmtsContainPragma*(n: AnyNode, w: TSpecialWord): bool = ## Deliberately NOT `getPragmaStmt(n, w) != nil`, and the reason is the one ## shape the `AnyNode` seam cannot serve: a proc that returns a node OR nil. ## `.bif` spells a missing child as a `DotToken` *inside* a tree, so there is ## no nil token to hand back as a return value, and a `Cursor` is not nilable. ## Predicates split out from such a proc are the way across. ## ## The duplicated traversal is the cost, and it is checked rather than ## trusted: `grindPredicates` asserts this answers exactly ## `getPragmaStmt(n, w) != nil` at every node, so the two cannot drift apart ## silently. case n.kind of nkStmtList: result = false for it in sons(n): if stmtsContainPragma(it, w): return true of nkPragma: result = false for it in sons(n): if whichPragma(it) == w: return true else: result = false proc hashString*(conf: ConfigRef; s: string): BiggestInt = # has to be the same algorithm as strmantle.hashString! if CPU[conf.target.targetCPU].bit == 64: # we have to use the same bitwidth # as the target CPU var b = 0'u64 for i in 0.. conf.target.floatSize * 3): result = true # requested anyway elif (tfFinal in pt.flags) and (pt.baseClass == nil): result = false # no need, because no subtyping possible else: result = true # ordinary objects are always passed by reference, # otherwise casting doesn't work of tyTuple: result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) else: result = false # first parameter and return type is 'lent T'? --> use pass by pointer if s.position == 0 and retType != nil and retType.kind == tyLent: result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or pt.kind == tySet and mapSetType(conf, pt) == ctArray) proc encodeName*(name: string): string = result = mangle(name) result = $result.len & result proc makeUnique(m: BModule; s: PSym, name: string = ""): string = result = if name == "": s.name.s else: name # keep backend-minted ids out of the `_u` namespace; their item counter # restarts at 0 and would collide with loaded symbols' ids. Which integer # identifies such a symbol is decided ONCE, in `astdef.backendMintedDisamb`, # shared with `mangleProcNameExt` and `ast2nif.toNifSymName`. if s.itemId.isBackendMinted: result.add "_c" result.add $backendMintedDisamb(s) else: result.add "_u" # Mirror `mangleProcNameExt`: use the per-(module,name) `disamb`, NOT # `itemId.item`. Under the per-module IC backend the same symbol is loaded # from a NIF in many processes and `itemId.item` is a fresh, load-order # dependent counter — so a method base would mangle to `_u1` in one module, # `_u3` in another and clean at its owner, none of which link. `disamb` is # assigned deterministically per (module, name) and is serialized, so every # process that touches the symbol derives the identical C name. result.add $s.disamb # module suffix LAST (a strippable trailing token; see `mangleProcNameExt`) result.add "__" result.add m.g.graph.ifaces[s.itemId.module].uniqueName proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string = #Module::Type var name = s.name.s & extra if makeUnique: name = makeUnique(m, s, name) "N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E" proc encodeType*(m: BModule; t: PType; staticLists: var string): string = result = "" var kindName = ($t.kind)[2..^1] kindName[0] = toLower($kindName[0])[0] case t.kind of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam: result = encodeSym(m, t.sym) of tyGenericInst, tyUserTypeClassInst, tyGenericBody: result = encodeName(t.genericHead.sym.name.s) result.add "I" for i in 1..