diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 471f36d891..6ee7d698ef 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -30,12 +30,6 @@ proc intLiteral(i: BiggestInt): Rope = else: result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc getStrLit(m: BModule, s: string): Rope = - discard cgsym(m, "TGenericSeq") - result = getTempName(m) - addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", - [result, makeCString(s), rope(len(s))]) - proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = if ty == nil: internalError(n.info, "genLiteral: ty is nil") case n.kind @@ -67,19 +61,9 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = of nkStrLit..nkTripleStrLit: case skipTypes(ty, abstractVarRange).kind of tyNil: - result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) + result = genNilStringLiteral(p.module, n.info) of tyString: - if n.strVal.isNil: - result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) - else: - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) - if id == p.module.labels: - # string literal not found in the cache: - result = ropecg(p.module, "((#NimStringDesc*) &$1)", - [getStrLit(p.module, n.strVal)]) - else: - result = ropecg(p.module, "((#NimStringDesc*) &$1$2)", - [p.module.tmpBase, rope(id)]) + result = genStringLiteral(p.module, n) else: if n.strVal.isNil: result = rope("NIM_NIL") else: result = makeCString(n.strVal) @@ -823,16 +807,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), p.module.labels) - let strLit = if id == p.module.labels: getStrLit(p.module, field.name.s) + let strLit = if id == p.module.labels: genStringLiteralDataOnly(p.module, field.name.s, e.info) else: p.module.tmpBase & rope(id) if op.magic == mNot: linefmt(p, cpsStmts, - "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if ($1) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) else: linefmt(p, cpsStmts, - "if (!($1)) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if (!($1)) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim new file mode 100644 index 0000000000..3011f0a944 --- /dev/null +++ b/compiler/ccgliterals.nim @@ -0,0 +1,81 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This include file contains the logic to produce constant string +## and seq literals. The code here is responsible that +## ``const x = ["a", "b"]`` works without hidden runtime creation code. +## The price is that seqs and strings are not purely a library +## implementation. + +template detectVersion(field, corename) = + if m.g.field == 0: + let core = getCompilerProc(corename) + if core == nil or core.kind != skConst: + m.g.field = 1 + else: + m.g.field = int ast.getInt(core.ast) + result = m.g.field + +proc detectStrVersion(m: BModule): int = + detectVersion(strVersion, "nimStrVersion") + +proc detectSeqVersion(m: BModule): int = + detectVersion(seqVersion, "nimSeqVersion") + +# ----- Version 1: GC'ed strings and seqs -------------------------------- + +proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope = + discard cgsym(m, "TGenericSeq") + result = getTempName(m) + addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", + [result, makeCString(s), rope(len(s))]) + +proc genStringLiteralV1(m: BModule; n: PNode): Rope = + if s.isNil: + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + else: + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + if id == m.labels: + # string literal not found in the cache: + result = ropecg(m, "((#NimStringDesc*) &$1)", + [genStringLiteralDataOnlyV1(m, n.strVal)]) + else: + result = ropecg(m, "((#NimStringDesc*) &$1$2)", + [m.tmpBase, rope(id)]) + +# ------ Version 2: destructor based strings and seqs ----------------------- + +proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = + discard "to implement" + +proc genStringLiteralV2(m: BModule; n: PNode): Rope = + discard "to implement" + +# ------ Version selector --------------------------------------------------- + +proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralDataOnlyV1(m, s) + of 2: result = genStringLiteralDataOnlyV2(m, s) + else: + localError(info, "cannot determine how to produce code for string literal") + +proc genStringLiteralFromData(m: BModule; data: Rope; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) &$1)", + [data]) + +proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + +proc genStringLiteral(m: BModule; n: PNode): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralV1(m, n) + of 2: result = genStringLiteralV2(m, n) + else: + localError(n.info, "cannot determine how to produce code for string literal") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index ed44c577df..2a4a105555 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -267,10 +267,6 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = if isDefined("checkabi"): addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))]) -proc getTempName(m: BModule): Rope = - result = m.tmpBase & rope(m.labels) - inc m.labels - proc ccgIntroducedPtr(s: PSym): bool = var pt = skipTypes(s.typ, typedescInst) assert skResult != s.kind @@ -316,8 +312,13 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyPointer: result = typeNameOrLiteral(m, typ, "void*") of tyString: - discard cgsym(m, "NimStringDesc") - result = typeNameOrLiteral(m, typ, "NimStringDesc*") + case detectStrVersion(m) + of 2: + discard cgsym(m, "string") + result = typeNameOrLiteral(m, typ, "NimStringV2") + else: + discard cgsym(m, "NimStringDesc") + result = typeNameOrLiteral(m, typ, "NimStringDesc*") of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING") of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL") of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR") diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 01610010b6..05f2225209 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -238,7 +238,12 @@ proc genProc(m: BModule, prc: PSym) template compileToCpp(m: BModule): untyped = gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags -include "ccgtypes.nim" +proc getTempName(m: BModule): Rope = + result = m.tmpBase & rope(m.labels) + inc m.labels + +include ccgliterals +include ccgtypes # ------------------------------ Manager of temporaries ------------------ @@ -551,11 +556,13 @@ proc loadDynamicLib(m: BModule, lib: PLib) = for i in countup(0, high(s)): inc(m.labels) if i > 0: add(loadlib, "||") - appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n", - [tmp, getStrLit(m, s[i])]) + let n = newStrNode(nkStrLit, s[i]) + n.info = lib.path.info + appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n", + [tmp, genStringLiteral(m, n)]) appcg(m, m.s[cfsDynLibInit], - "if (!($1)) #nimLoadLibraryError((#NimStringDesc*) &$2);$n", - [loadlib, getStrLit(m, lib.path.strVal)]) + "if (!($1)) #nimLoadLibraryError($2);$n", + [loadlib, genStringLiteral(m, lib.path)]) else: var p = newProc(nil, m) p.options = p.options - {optStackTrace, optEndb} diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index efa346934f..f8167acdca 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -70,7 +70,7 @@ type threadVarAccessed*: bool # true if the proc already accessed some threadvar lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements currLineInfo*: TLineInfo # AST codegen will make this superfluous - nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] + nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] # in how many nested try statements we are # (the vars must be volatile then) # bool is true when are in the except part of a try block @@ -116,6 +116,7 @@ type breakpoints*: Rope # later the breakpoints are inserted into the main proc typeInfoMarker*: TypeCache config*: ConfigRef + strVersion*, seqVersion*: int # version of the string/seq implementation to use TCGen = object of TPassContext # represents a C source file s*: TCFileSections # sections of the C file diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1295ee18df..78ded578f9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -65,7 +65,7 @@ const wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, - wIntDefine, wStrDefine, wUsed} + wIntDefine, wStrDefine, wUsed, wCompilerProc, wCore} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index c32cf3690b..02c1928516 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -15,11 +15,11 @@ type len, cap: int data: ptr UncheckedArray[T] +const nimSeqVersion {.core.} = 2 + template frees(s) = dealloc(s.data, s.cap * sizeof(T)) # XXX make code memory safe for overflows in '*' -proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} = - seq[T](len: x.len, cap: x.len, data: x) when defined(nimHasTrace): proc `=trace`[T](s: seq[T]; a: Allocator) = @@ -120,7 +120,7 @@ proc `$`*[T](x: seq[T]): string = result = "@[" var firstElement = true for i in 0.. 0: result.data = alloc0(len+1) -converter toCString(x: string): cstring {.core.} = +converter toCString(x: string): cstring {.core, inline.} = if x.len == 0: cstring"" else: cast[cstring](x.data) proc newStringOfCap*(cap: int): string =